Browse Source

Merge branch 'next' of git://github.com/blackberry/GamePlay into cmake-windows

Conflicts:
	template/template.vcxproj
	template/template.vcxproj.filters
	template/template.vcxproj.user
Marc Plano-Lesay 12 years ago
parent
commit
5d8b1a2f6b
100 changed files with 10660 additions and 11105 deletions
  1. 208 207
      .gitignore
  2. 6 0
      CHANGES.md
  3. 1 5
      CMakeLists.txt
  4. 3 0
      api/README.md
  5. 0 0
      api/header.html
  6. 0 0
      codestyle.xml
  7. 0 5
      gameplay-api/gameplay.html
  8. 0 101
      gameplay-encoder/src/DAEChannelTarget.cpp
  9. 0 84
      gameplay-encoder/src/DAEChannelTarget.h
  10. 0 78
      gameplay-encoder/src/DAEOptimizer.cpp
  11. 0 49
      gameplay-encoder/src/DAEOptimizer.h
  12. 0 2024
      gameplay-encoder/src/DAESceneEncoder.cpp
  13. 0 211
      gameplay-encoder/src/DAESceneEncoder.h
  14. 0 554
      gameplay-encoder/src/DAEUtil.cpp
  15. 0 203
      gameplay-encoder/src/DAEUtil.h
  16. 0 183
      gameplay-template/res/box.dae
  17. BIN
      gameplay-template/res/box.gpb
  18. 2 2
      gameplay.doxyfile
  19. 8 8
      gameplay.sln
  20. 0 30
      gameplay.workspace
  21. 9 9
      gameplay.xcworkspace/contents.xcworkspacedata
  22. 7 0
      gameplay/CMakeLists.txt
  23. 2 5
      gameplay/android/build.xml
  24. 5 14
      gameplay/android/jni/Android.mk
  25. 22 9
      gameplay/gameplay.vcxproj
  26. 64 28
      gameplay/gameplay.vcxproj.filters
  27. 431 426
      gameplay/gameplay.xcodeproj/project.pbxproj
  28. 7 0
      gameplay/gameplay.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  29. BIN
      gameplay/gameplay.xcodeproj/project.xcworkspace/xcuserdata/mozeal.xcuserdatad/UserInterfaceState.xcuserstate
  30. 1 1
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-ios.xcscheme
  31. 1 1
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-macosx.xcscheme
  32. 1 1
      gameplay/res/shaders/colored.vert
  33. 12 1
      gameplay/src/AIStateMachine.cpp
  34. 1 1
      gameplay/src/AbsoluteLayout.cpp
  35. 3 1
      gameplay/src/Animation.cpp
  36. 69 36
      gameplay/src/AnimationClip.cpp
  37. 17 0
      gameplay/src/AnimationClip.h
  38. 3 1
      gameplay/src/AnimationController.cpp
  39. 37 7
      gameplay/src/Button.cpp
  40. 7 0
      gameplay/src/Button.h
  41. 41 22
      gameplay/src/Camera.cpp
  42. 21 1
      gameplay/src/Camera.h
  43. 20 0
      gameplay/src/CheckBox.cpp
  44. 7 0
      gameplay/src/CheckBox.h
  45. 562 51
      gameplay/src/Container.cpp
  46. 54 4
      gameplay/src/Container.h
  47. 77 35
      gameplay/src/Control.cpp
  48. 37 17
      gameplay/src/Control.h
  49. 79 18
      gameplay/src/Curve.cpp
  50. 34 2
      gameplay/src/Curve.h
  51. 70 26
      gameplay/src/FileSystem.cpp
  52. 163 127
      gameplay/src/Form.cpp
  53. 17 0
      gameplay/src/Form.h
  54. 23 2
      gameplay/src/FrameBuffer.cpp
  55. 22 0
      gameplay/src/FrameBuffer.h
  56. 16 4
      gameplay/src/Frustum.cpp
  57. 21 1
      gameplay/src/Frustum.h
  58. 56 21
      gameplay/src/Game.cpp
  59. 40 4
      gameplay/src/Game.h
  60. 10 0
      gameplay/src/Game.inl
  61. 67 9
      gameplay/src/Gamepad.cpp
  62. 25 23
      gameplay/src/Gamepad.h
  63. 1 1
      gameplay/src/Image.h
  64. 148 0
      gameplay/src/ImageControl.cpp
  65. 146 0
      gameplay/src/ImageControl.h
  66. 89 3
      gameplay/src/Joint.cpp
  67. 26 7
      gameplay/src/Joint.h
  68. 6 8
      gameplay/src/Joystick.cpp
  69. 9 7
      gameplay/src/Label.cpp
  70. 85 0
      gameplay/src/Light.cpp
  71. 14 0
      gameplay/src/Light.h
  72. 5 0
      gameplay/src/MaterialParameter.cpp
  73. 14 4
      gameplay/src/MaterialParameter.h
  74. 1 1
      gameplay/src/Matrix.h
  75. 66 0
      gameplay/src/MeshBatch.cpp
  76. 26 2
      gameplay/src/MeshBatch.h
  77. 2 57
      gameplay/src/MeshBatch.inl
  78. 2 2
      gameplay/src/MeshSkin.cpp
  79. 1 0
      gameplay/src/MeshSkin.h
  80. 1 0
      gameplay/src/Model.h
  81. 42 8
      gameplay/src/Node.cpp
  82. 2 1
      gameplay/src/Node.h
  83. 4 4
      gameplay/src/Pass.cpp
  84. 78 0
      gameplay/src/Platform.cpp
  85. 37 2
      gameplay/src/Platform.h
  86. 1404 1419
      gameplay/src/PlatformAndroid.cpp
  87. 1587 1599
      gameplay/src/PlatformBlackBerry.cpp
  88. 1460 993
      gameplay/src/PlatformLinux.cpp
  89. 2286 2057
      gameplay/src/PlatformMacOSX.mm
  90. 71 59
      gameplay/src/PlatformWindows.cpp
  91. 23 45
      gameplay/src/PlatformiOS.mm
  92. 24 3
      gameplay/src/RadioButton.cpp
  93. 7 0
      gameplay/src/RadioButton.h
  94. 199 70
      gameplay/src/RenderState.cpp
  95. 34 0
      gameplay/src/RenderState.h
  96. 82 0
      gameplay/src/Scene.cpp
  97. 19 17
      gameplay/src/Scene.h
  98. 20 0
      gameplay/src/SceneLoader.cpp
  99. 7 6
      gameplay/src/SceneLoader.h
  100. 243 78
      gameplay/src/ScriptController.cpp

+ 208 - 207
.gitignore

@@ -23,17 +23,9 @@ Thumbs.db
 /Device-Profile
 /Device-Release
 /gameplay-internal
-/gameplay-api/xml
-
-/gameplay-encoder/Debug
-/gameplay-encoder/Release
-/gameplay-encoder/gameplay-encoder.xcodeproj/xcuserdata
-
-/gameplay-luagen/Release
-/gameplay-luagen/Debug
-/gameplay-luagen/doxygen_entrydb_680.tmp
-/gameplay-luagen/doxygen_objdb_680.tmp
-/gameplay-luagen/xml
+/api/xml
+/bin
+/external-deps
 
 /gameplay/Debug
 /gameplay/DebugMem
@@ -45,8 +37,6 @@ Thumbs.db
 /gameplay/Device-Coverage
 /gameplay/Device-Profile
 /gameplay/Device-Release
-/gameplay.xcworkspace/xcuserdata
-/gameplay/gameplay.xcodeproj/xcuserdata
 /gameplay/windows
 /gameplay/android/NUL
 /gameplay/android/proguard.cfg
@@ -54,208 +44,219 @@ Thumbs.db
 /gameplay/android/local.properties
 /gameplay/android/project.properties
 /gameplay/android/obj
+/gameplay.xcworkspace/xcuserdata
+/gameplay/gameplay.xcodeproj/xcuserdata
 
-/gameplay-tests/Debug
-/gameplay-tests/DebugMem
-/gameplay-tests/Release
-/gameplay-tests/Simulator
-/gameplay-tests/Simulator-Coverage
-/gameplay-tests/Simulator-Profile
-/gameplay-tests/Device-Debug
-/gameplay-tests/Device-Coverage
-/gameplay-tests/Device-Profile
-/gameplay-tests/Device-Release
-/gameplay-tests/res/shaders
-/gameplay-tests/res/logo_powered_white.png
-/gameplay-tests/gameplay-tests.xcodeproj/xcuserdata
-/gameplay-tests/android/src
-/gameplay-tests/android/assets
-/gameplay-tests/android/bin
-/gameplay-tests/android/gen
-/gameplay-tests/android/libs
-/gameplay-tests/android/obj
-/gameplay-tests/android/NUL
-/gameplay-tests/android/local.properties
-/gameplay-tests/android/proguard-project.txt
-/gameplay-tests/android/project.properties
+/tools/gameplay-encoder/Debug
+/tools/gameplay-encoder/Release
+/tools/gameplay-encoder/gameplay-encoder.xcodeproj/xcuserdata
 
-/gameplay-samples/sample00-mesh/Debug
-/gameplay-samples/sample00-mesh/DebugMem
-/gameplay-samples/sample00-mesh/Release
-/gameplay-samples/sample00-mesh/Simulator
-/gameplay-samples/sample00-mesh/Simulator-Coverage
-/gameplay-samples/sample00-mesh/Simulator-Profile
-/gameplay-samples/sample00-mesh/Device-Debug
-/gameplay-samples/sample00-mesh/Device-Coverage
-/gameplay-samples/sample00-mesh/Device-Profile
-/gameplay-samples/sample00-mesh/Device-Release
-/gameplay-samples/sample00-mesh/res/shaders
-/gameplay-samples/sample00-mesh/res/logo_powered_white.png
-/gameplay-samples/sample00-mesh/sample00-mesh.xcodeproj/xcuserdata
-/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-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
+/tools/gameplay-luagen/Release
+/tools/gameplay-luagen/Debug
+/tools/gameplay-luagen/gameplay-luagen.xcodeproj/xcuserdata
+/tools/gameplay-luagen/doxygen_entrydb_680.tmp
+/tools/gameplay-luagen/doxygen_objdb_680.tmp
+/tools/gameplay-luagen/xml
 
-/gameplay-samples/sample01-longboard/Debug
-/gameplay-samples/sample01-longboard/DebugMem
-/gameplay-samples/sample01-longboard/Release
-/gameplay-samples/sample01-longboard/Simulator
-/gameplay-samples/sample01-longboard/Simulator-Coverage
-/gameplay-samples/sample01-longboard/Simulator-Profile
-/gameplay-samples/sample01-longboard/Device-Debug
-/gameplay-samples/sample01-longboard/Device-Coverage
-/gameplay-samples/sample01-longboard/Device-Profile
-/gameplay-samples/sample01-longboard/Device-Release
-/gameplay-samples/sample01-longboard/res/shaders
-/gameplay-samples/sample01-longboard/res/logo_powered_white.png
-/gameplay-samples/sample01-longboard/sample01-longboard.xcodeproj/xcuserdata
-/gameplay-samples/sample01-longboard/NUL
-/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/proguard.cfg
-/gameplay-samples/sample01-longboard/android/project.properties
-/gameplay-samples/sample01-longboard/android/local.properties
+/samples/browser/Debug
+/samples/browser/DebugMem
+/samples/browser/Release
+/samples/browser/Simulator
+/samples/browser/Simulator-Coverage
+/samples/browser/Simulator-Profile
+/samples/browser/Device-Debug
+/samples/browser/Device-Coverage
+/samples/browser/Device-Profile
+/samples/browser/Device-Release
+/samples/browser/res/shaders
+/samples/browser/res/logo_powered_white.png
+/samples/browser/android/src
+/samples/browser/android/assets
+/samples/browser/android/bin
+/samples/browser/android/gen
+/samples/browser/android/libs
+/samples/browser/android/obj
+/samples/browser/android/NUL
+/samples/browser/android/local.properties
+/samples/browser/android/proguard-project.txt
+/samples/browser/android/project.properties
+/samples/browser/sample-browser.xcodeproj/xcuserdata
 
-/gameplay-samples/sample02-spaceship/Debug
-/gameplay-samples/sample02-spaceship/DebugMem
-/gameplay-samples/sample02-spaceship/Release
-/gameplay-samples/sample02-spaceship/Simulator
-/gameplay-samples/sample02-spaceship/Simulator-Coverage
-/gameplay-samples/sample02-spaceship/Simulator-Profile
-/gameplay-samples/sample02-spaceship/Device-Debug
-/gameplay-samples/sample02-spaceship/Device-Coverage
-/gameplay-samples/sample02-spaceship/Device-Profile
-/gameplay-samples/sample02-spaceship/Device-Release
-/gameplay-samples/sample02-spaceship/res/shaders
-/gameplay-samples/sample02-spaceship/res/logo_powered_white.png
-/gameplay-samples/sample02-spaceship/sample02-spaceship.xcodeproj/xcuserdata
-/gameplay-samples/sample02-spaceship/android/NUL
-/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
+/samples/character/Debug
+/samples/character/DebugMem
+/samples/character/Release
+/samples/character/Simulator
+/samples/character/Simulator-Coverage
+/samples/character/Simulator-Profile
+/samples/character/Device-Debug
+/samples/character/Device-Coverage
+/samples/character/Device-Profile
+/samples/character/Device-Release
+/samples/character/game.config
+/samples/character/res/shaders
+/samples/character/res/logo_powered_white.png
+/samples/character/android/project.properties
+/samples/character/android/proguard.cfg
+/samples/character/android/local.properties
+/samples/character/android/src
+/samples/character/android/assets
+/samples/character/android/bin
+/samples/character/android/gen
+/samples/character/android/libs
+/samples/character/android/obj
+/samples/character/android/NUL
+/samples/character/res/gamepad.xcf
+/samples/character/sample-character.xcodeproj/xcuserdata
 
-/gameplay-samples/sample03-character/Debug
-/gameplay-samples/sample03-character/DebugMem
-/gameplay-samples/sample03-character/Release
-/gameplay-samples/sample03-character/Simulator
-/gameplay-samples/sample03-character/Simulator-Coverage
-/gameplay-samples/sample03-character/Simulator-Profile
-/gameplay-samples/sample03-character/Device-Debug
-/gameplay-samples/sample03-character/Device-Coverage
-/gameplay-samples/sample03-character/Device-Profile
-/gameplay-samples/sample03-character/Device-Release
-/gameplay-samples/sample03-character/game.config
-/gameplay-samples/sample03-character/res/shaders
-/gameplay-samples/sample03-character/res/logo_powered_white.png
-/gameplay-samples/sample03-character/sample03-character.xcodeproj/xcuserdata
-/gameplay-samples/sample03-character/android/project.properties
-/gameplay-samples/sample03-character/android/proguard.cfg
-/gameplay-samples/sample03-character/android/local.properties
-/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/sample03-character/res/gamepad.xcf
+/samples/longboard/Debug
+/samples/longboard/DebugMem
+/samples/longboard/Release
+/samples/longboard/Simulator
+/samples/longboard/Simulator-Coverage
+/samples/longboard/Simulator-Profile
+/samples/longboard/Device-Debug
+/samples/longboard/Device-Coverage
+/samples/longboard/Device-Profile
+/samples/longboard/Device-Release
+/samples/longboard/res/shaders
+/samples/longboard/res/logo_powered_white.png
+/samples/longboard/NUL
+/samples/longboard/android/NUL
+/samples/longboard/android/src
+/samples/longboard/android/assets
+/samples/longboard/android/bin
+/samples/longboard/android/gen
+/samples/longboard/android/libs
+/samples/longboard/android/obj
+/samples/longboard/android/proguard.cfg
+/samples/longboard/android/project.properties
+/samples/longboard/android/local.properties
+/samples/longboard/sample-longboard.xcodeproj/xcuserdata
 
-/gameplay-samples/sample04-particles/Debug
-/gameplay-samples/sample04-particles/DebugMem
-/gameplay-samples/sample04-particles/Release
-/gameplay-samples/sample04-particles/Simulator
-/gameplay-samples/sample04-particles/Simulator-Coverage
-/gameplay-samples/sample04-particles/Simulator-Profile
-/gameplay-samples/sample04-particles/Device-Debug
-/gameplay-samples/sample04-particles/Device-Coverage
-/gameplay-samples/sample04-particles/Device-Profile
-/gameplay-samples/sample04-particles/Device-Release
-/gameplay-samples/sample04-particles/res/shaders
-/gameplay-samples/sample04-particles/res/logo_powered_white.png
-/gameplay-samples/sample04-particles/sample04-particles.xcodeproj/xcuserdata
-/gameplay-samples/sample04-particles/Device-Debug
-/gameplay-samples/sample04-particles/Debug
-/gameplay-samples/sample04-particles/DebugMem
-/gameplay-samples/sample04-particles/android/src
-/gameplay-samples/sample04-particles/android/assets
-/gameplay-samples/sample04-particles/android/bin
-/gameplay-samples/sample04-particles/android/gen
-/gameplay-samples/sample04-particles/android/libs
-/gameplay-samples/sample04-particles/android/obj
-/gameplay-samples/sample04-particles/android/proguard.cfg
-/gameplay-samples/sample04-particles/android/local.properties
-/gameplay-samples/sample04-particles/android/project.properties
+/samples/lua/Debug
+/samples/lua/DebugMem
+/samples/lua/Release
+/samples/lua/Simulator
+/samples/lua/Simulator-Coverage
+/samples/lua/Simulator-Profile
+/samples/lua/Device-Debug
+/samples/lua/Device-Coverage
+/samples/lua/Device-Profile
+/samples/lua/Device-Release
+/samples/lua/res/shaders
+/samples/lua/res/logo_powered_white.png
+/samples/lua/android/src
+/samples/lua/android/assets
+/samples/lua/android/bin
+/samples/lua/android/gen
+/samples/lua/android/libs
+/samples/lua/android/obj
+/samples/lua/android/NUL
+/samples/lua/android/local.properties
+/samples/lua/android/proguard.cfg
+/samples/lua/android/project.properties
+/samples/lua/sample-lua.xcodeproj/xcuserdata
 
-/gameplay-samples/sample05-lua/Debug
-/gameplay-samples/sample05-lua/DebugMem
-/gameplay-samples/sample05-lua/Release
-/gameplay-samples/sample05-lua/Simulator
-/gameplay-samples/sample05-lua/Simulator-Coverage
-/gameplay-samples/sample05-lua/Simulator-Profile
-/gameplay-samples/sample05-lua/Device-Debug
-/gameplay-samples/sample05-lua/Device-Coverage
-/gameplay-samples/sample05-lua/Device-Profile
-/gameplay-samples/sample05-lua/Device-Release
-/gameplay-samples/sample05-lua/res/shaders
-/gameplay-samples/sample05-lua/res/logo_powered_white.png
-/gameplay-samples/sample05-lua/sample05-lua.xcodeproj/xcuserdata
-/gameplay-samples/sample05-lua/android/src
-/gameplay-samples/sample05-lua/android/assets
-/gameplay-samples/sample05-lua/android/bin
-/gameplay-samples/sample05-lua/android/gen
-/gameplay-samples/sample05-lua/android/libs
-/gameplay-samples/sample05-lua/android/obj
-/gameplay-samples/sample05-lua/android/NUL
-/gameplay-samples/sample05-lua/android/local.properties
-/gameplay-samples/sample05-lua/android/proguard.cfg
-/gameplay-samples/sample05-lua/android/project.properties
+/samples/mesh/Debug
+/samples/mesh/DebugMem
+/samples/mesh/Release
+/samples/mesh/Simulator
+/samples/mesh/Simulator-Coverage
+/samples/mesh/Simulator-Profile
+/samples/mesh/Device-Debug
+/samples/mesh/Device-Coverage
+/samples/mesh/Device-Profile
+/samples/mesh/Device-Release
+/samples/mesh/res/shaders
+/samples/mesh/res/logo_powered_white.png
+/samples/mesh/android/src
+/samples/mesh/android/assets
+/samples/mesh/android/bin
+/samples/mesh/android/gen
+/samples/mesh/android/libs
+/samples/mesh/android/obj
+/samples/mesh/android/NUL
+/samples/mesh/android/local.properties
+/samples/mesh/android/proguard.cfg
+/samples/mesh/android/project.properties
+/samples/mesh/sample-mesh.xcodeproj/xcuserdata
 
-/gameplay-samples/sample06-racer/Debug
-/gameplay-samples/sample06-racer/DebugMem
-/gameplay-samples/sample06-racer/Release
-/gameplay-samples/sample06-racer/Simulator
-/gameplay-samples/sample06-racer/Simulator-Coverage
-/gameplay-samples/sample06-racer/Simulator-Profile
-/gameplay-samples/sample06-racer/Device-Debug
-/gameplay-samples/sample06-racer/Device-Debug-IMG
-/gameplay-samples/sample06-racer/Device-Coverage
-/gameplay-samples/sample06-racer/Device-Profile
-/gameplay-samples/sample06-racer/Device-Release
-/gameplay-samples/sample06-racer/game.config
-/gameplay-samples/sample06-racer/res/shaders
-/gameplay-samples/sample06-racer/res/logo_powered_white.png
-/gameplay-samples/sample06-racer/sample06-racer.xcodeproj/xcuserdata
-/gameplay-samples/sample06-racer/android/project.properties
-/gameplay-samples/sample06-racer/android/proguard.cfg
-/gameplay-samples/sample06-racer/android/local.properties
-/gameplay-samples/sample06-racer/android/src
-/gameplay-samples/sample06-racer/android/assets
-/gameplay-samples/sample06-racer/android/bin
-/gameplay-samples/sample06-racer/android/gen
-/gameplay-samples/sample06-racer/android/libs
-/gameplay-samples/sample06-racer/android/obj
-/gameplay-samples/sample06-racer/android/NUL
-/gameplay-samples/sample06-racer/android/local.properties
-/gameplay-samples/sample06-racer/android/proguard.cfg
-/gameplay-samples/sample06-racer/android/project.properties
+/samples/particles/Debug
+/samples/particles/DebugMem
+/samples/particles/Release
+/samples/particles/Simulator
+/samples/particles/Simulator-Coverage
+/samples/particles/Simulator-Profile
+/samples/particles/Device-Debug
+/samples/particles/Device-Coverage
+/samples/particles/Device-Profile
+/samples/particles/Device-Release
+/samples/particles/res/shaders
+/samples/particles/res/logo_powered_white.png
+/samples/particles/Device-Debug
+/samples/particles/Debug
+/samples/particles/DebugMem
+/samples/particles/android/src
+/samples/particles/android/assets
+/samples/particles/android/bin
+/samples/particles/android/gen
+/samples/particles/android/libs
+/samples/particles/android/obj
+/samples/particles/android/proguard.cfg
+/samples/particles/android/local.properties
+/samples/particles/android/project.properties
+/samples/particles/sample-particles.xcodeproj/xcuserdata
 
+/samples/racer/Debug
+/samples/racer/DebugMem
+/samples/racer/Release
+/samples/racer/Simulator
+/samples/racer/Simulator-Coverage
+/samples/racer/Simulator-Profile
+/samples/racer/Device-Debug
+/samples/racer/Device-Debug-IMG
+/samples/racer/Device-Coverage
+/samples/racer/Device-Profile
+/samples/racer/Device-Release
+/samples/racer/game.config
+/samples/racer/res/shaders
+/samples/racer/res/logo_powered_white.png
+/samples/racer/android/project.properties
+/samples/racer/android/proguard.cfg
+/samples/racer/android/local.properties
+/samples/racer/android/src
+/samples/racer/android/assets
+/samples/racer/android/bin
+/samples/racer/android/gen
+/samples/racer/android/libs
+/samples/racer/android/obj
+/samples/racer/android/NUL
+/samples/racer/android/local.properties
+/samples/racer/android/proguard.cfg
+/samples/racer/android/project.properties
+/samples/racer/sample-racer.xcodeproj/xcuserdata
 
+/samples/spaceship/Debug
+/samples/spaceship/DebugMem
+/samples/spaceship/Release
+/samples/spaceship/Simulator
+/samples/spaceship/Simulator-Coverage
+/samples/spaceship/Simulator-Profile
+/samples/spaceship/Device-Debug
+/samples/spaceship/Device-Coverage
+/samples/spaceship/Device-Profile
+/samples/spaceship/Device-Release
+/samples/spaceship/res/shaders
+/samples/spaceship/res/logo_powered_white.png
+/samples/spaceship/android/NUL
+/samples/spaceship/android/project.properties
+/samples/spaceship/android/assets
+/samples/spaceship/android/bin
+/samples/spaceship/android/gen
+/samples/spaceship/android/libs
+/samples/spaceship/android/obj
+/samples/spaceship/android/src
+/samples/spaceship/android/proguard.cfg
+/samples/spaceship/android/local.properties
+/samples/spaceship/sample-spaceship.xcodeproj/xcuserdata
 

+ 6 - 0
CHANGES.md

@@ -1,3 +1,9 @@
+## v1.7.0
+
+- Adds a lua function "convert(object, className)" that will convert a gameplay userdata object to another class type by changing the metatable. (For example: This lets you convert Control to Button in lua)
+- Repo directory restructure dropping gameplay- prefix on many folders and files.
+- Removed pre-compiled api/html docs to reduce repository bloat.
+
 ## v1.6.0
 
 - Adds file Stream interface for reading/writing files instead of using fread/fwrite. 

+ 1 - 5
CMakeLists.txt

@@ -22,12 +22,8 @@ endif()
 add_subdirectory(gameplay)
 
 # gameplay samples
-add_subdirectory(gameplay-samples)
+add_subdirectory(samples)
 
-# gameplay samples
-add_subdirectory(gameplay-tests)
 
-# gameplay encoder (See gameplay/bin)
-#add_subdirectory(gameplay-encoder)
 
 

+ 3 - 0
api/README.md

@@ -0,0 +1,3 @@
+This folder is used to generate the doxygen api docs.
+
+

+ 0 - 0
gameplay-api/header.html → api/header.html


+ 0 - 0
gameplay-codestyle.xml → codestyle.xml


+ 0 - 5
gameplay-api/gameplay.html

@@ -1,5 +0,0 @@
-<HTML>
-<HEAD>
-<META HTTP-EQUIV="Refresh" CONTENT="0; URL=html/index.html">
-</HEAD>
-</HTML>

+ 0 - 101
gameplay-encoder/src/DAEChannelTarget.cpp

@@ -1,101 +0,0 @@
-
-#include "Base.h"
-#include "DAEChannelTarget.h"
-
-namespace gameplay
-{
-
-DAEChannelTarget::DAEChannelTarget(const domChannelRef channelRef) : _channel(channelRef), _targetElement(NULL)
-{
-    const std::string target = channelRef->getTarget();
-    size_t index = target.find('/');
-    if (index == std::string::npos)
-    {
-        // If the string doesn't contain a '/' then the whole string is the id
-        // and there are no sid's being targeted.
-        _targetId = target;
-    }
-    else
-    {
-        // The targetId is the part before the first '/'
-        _targetId = target.substr(0, index);
-
-        // each '/' denotes another sid
-        size_t start;
-        size_t end;
-        do
-        {
-            start = index + 1;
-            end = target.find('/', start);
-        
-            std::string sub;
-            if (end == std::string::npos)
-            {
-                sub = target.substr(start);
-                // break;
-            }
-            else
-            {
-                sub = target.substr(start, end - start);
-                index = end + 1;
-            }
-            _attributeIds.push_back(sub);
-        } while (end != std::string::npos);
-    }
-
-}
-
-DAEChannelTarget::~DAEChannelTarget(void)
-{
-}
-
-daeElement* DAEChannelTarget::getTargetElement()
-{
-    if (!_targetElement && _targetId.length() > 0)
-    {
-        daeSIDResolver resolver(_channel->getDocument()->getDomRoot(), _targetId.c_str());
-        _targetElement = resolver.getElement();
-    }
-    return _targetElement;
-}
-
-const std::string& DAEChannelTarget::getTargetId() const
-{
-    return _targetId;
-}
-
-size_t DAEChannelTarget::getTargetAttributeCount() const
-{
-    return _attributeIds.size();
-}
-
-daeElement* DAEChannelTarget::getTargetAttribute(size_t index)
-{
-    if (index >= _attributeIds.size())
-    {
-        return NULL;
-    }
-    const std::string& att = _attributeIds[index];
-    std::string sid = att.substr(0, att.find('.'));
-    daeSIDResolver resolver(getTargetElement(), sid.c_str());
-    return resolver.getElement();
-}
-
-void DAEChannelTarget::getPropertyName(size_t index, std::string* str)
-{
-    if (index < _attributeIds.size())
-    {
-        // The property name is the string segment after the '.'
-        // The propery is optional so it might not be found.
-        const std::string& att = _attributeIds[index];
-        size_t i = att.find('.');
-        if (i != std::string::npos && i < att.size())
-        {
-            str->assign(att.substr(i+1));
-            return;
-        }
-    }
-    str->clear();
-}
-
-}

+ 0 - 84
gameplay-encoder/src/DAEChannelTarget.h

@@ -1,84 +0,0 @@
-#ifndef DAECHANNELTARGET_H_
-#define DAECHANNELTARGET_H_
-
-namespace gameplay
-{
-
-/**
- * DAEChannelTarget represents the target attribute of the COLLADA dom element "channel".
- * The syntax for the channel target is in the COLLADA spec under "COLLADA Target Addressing".
- */
-class DAEChannelTarget
-{
-public:
-
-    /**
-     * Constructs the DAEChannelTarget from the given channel element.
-     */
-    DAEChannelTarget(const domChannelRef channelRef);
-
-    /**
-     * Destructor.
-     */
-    virtual ~DAEChannelTarget(void);
-
-    /**
-     * Returns a pointer to the dom element that this targeted.
-     * 
-     * @return Pointer to the dom element or NULL if not found.
-     */
-    daeElement* getTargetElement();
-
-    /**
-     * Returns the target ID string.
-     */
-    const std::string& getTargetId() const;
-
-    /**
-     * Returns the number of target attributes for this channel target.
-     */
-    size_t getTargetAttributeCount() const;
-
-    /**
-     * Returns the attribute element at the given index.
-     */
-    daeElement* getTargetAttribute(size_t index);
-
-    /**
-     * Returns property name of the attribute at the given index.
-     * The property name is copied to str.
-     * If the attribute is not found or it doesn't have a property, str is cleared.
-     * 
-     * @param index Index of the attribute.
-     * @param str Destination string to copy the property name to.
-     */
-    void getPropertyName(size_t index, std::string* str);
-
-private:
-    
-    /**
-     * The channel element.
-     */
-    const domChannelRef _channel;
-
-    domElement* _targetElement;
-
-    /**
-     * The first part is the id attribute of an element in the instance document
-     * or a dot segment (".") indicating that this is a relative address.
-     */
-    std::string _targetId;
-
-    /**
-     * A channel target can have zero or more target attributes.
-     * Each target attribute my have a property.
-     * Example: "cube/Translate.X/Translate.Y"
-     * Result: attributeIds will contain 2 elements. "Translate.X" and "Translate.Y"
-     * Refer to the COLLADA spec "COLLADA Target Addressing".
-     */
-    std::vector<std::string> _attributeIds;
-};
-
-}
-
-#endif

+ 0 - 78
gameplay-encoder/src/DAEOptimizer.cpp

@@ -1,78 +0,0 @@
-#include "Base.h"
-#include "DAEOptimizer.h"
-
-namespace gameplay
-{
-
-DAEOptimizer::DAEOptimizer(domCOLLADA* dom)
-{
-    _dom = dom;
-}
-
-DAEOptimizer::~DAEOptimizer()
-{   
-}
-
-void DAEOptimizer::combineAnimations(const std::string& nodeId, const std::string& animationId)
-{
-    std::list<domChannelRef> channels;
-
-    daeSIDResolver resolver(_dom, nodeId.c_str());
-    daeElement* element = resolver.getElement();
-    if (element && element->typeID() == domNode::ID())
-    {
-        domNodeRef node = daeSafeCast<domNode>(resolver.getElement());
-        getAnimationChannels(node, channels);
-    }
-
-    // Get the <library_animations>
-    domLibrary_animations_Array& animationsLibraryArray = _dom->getLibrary_animations_array();
-    assert(animationsLibraryArray.getCount() > 0);
-    domLibrary_animationsRef& animationsLibrary = animationsLibraryArray.get(0);
-
-    // Add a new animation
-    domAnimationRef animation = daeSafeCast<domAnimation>(animationsLibrary->createAndPlace("animation"));
-    assert(animation);
-    animation->setId(animationId.c_str());
-    // TODO: Make sure that there doesn't already exist an animation with this ID.
-
-    // Move each of the channels to this animation
-    for (std::list<domChannelRef>::iterator i = channels.begin(); i != channels.end(); ++i)
-    {
-        moveChannelAndSouresToAnimation(*i, animation);
-    }
-
-    // Clean up the empty animations
-    deleteEmptyAnimations();
-}
-
-void DAEOptimizer::deleteEmptyAnimations()
-{
-    std::list<domAnimationRef> animations;
-    
-    // Get the list of empty animations
-    domLibrary_animations_Array& animationLibrary = _dom->getLibrary_animations_array();
-    size_t animationLibraryCount = animationLibrary.getCount();
-    for (size_t i = 0; i < animationLibraryCount; ++i)
-    {
-        domLibrary_animationsRef& animationsRef = animationLibrary.get(i);
-        domAnimation_Array& animationArray = animationsRef->getAnimation_array();
-        size_t animationCount = animationArray.getCount();
-        for (size_t j = 0; j < animationCount; ++j)
-        {
-            domAnimationRef& animation = animationArray.get(j);
-            if (isEmptyAnimation(animation))
-            {
-                animations.push_back(animation);
-            }
-        }
-    }
-
-    // Delete all of the empty animations
-    for (std::list<domAnimationRef>::iterator i = animations.begin(); i != animations.end(); ++i)
-    {
-        daeElement::removeFromParent(*i);
-    }
-}
-
-}

+ 0 - 49
gameplay-encoder/src/DAEOptimizer.h

@@ -1,49 +0,0 @@
-#ifndef DAEOPTIMIZER_H_
-#define DAEOPTIMIZER_H_
-
-#include "DAEUtil.h"
-
-namespace gameplay
-{
-
-/**
- * The DAEOptimizer optimizes a COLLADA dom.
- */
-class DAEOptimizer
-{
-public:
-
-    /**
-     * Constructor.
-     */
-    DAEOptimizer(domCOLLADA* dom);
-
-    /**
-     * Destructor.
-     */
-    ~DAEOptimizer(void);
-
-    /**
-     * Combines all of the animations that target the node and all of its child nodes into a new animation with the given ID.
-     * 
-     * @param nodeId The ID of the node.
-     * @param animationId The ID of the new animation to create.
-     */
-    void combineAnimations(const std::string& nodeId, const std::string& animationId);
-
-private:
-
-    /**
-     * Deletes all of the empty animations in the dom.
-     */
-    void deleteEmptyAnimations();
-
-private:
-    
-    domCOLLADA* _dom;
-    std::string _inputPath;
-};
-
-}
-
-#endif

+ 0 - 2024
gameplay-encoder/src/DAESceneEncoder.cpp

@@ -1,2024 +0,0 @@
-/*
- * DAESceneEncoder.h
- */
-#include "Base.h"
-
-#include "DAESceneEncoder.h"
-#include "DAEOptimizer.h"
-
-//#define ENCODER_PRINT_TIME 1
-
-namespace gameplay
-{
-
-DAESceneEncoder::DAESceneEncoder()
-    : _collada(NULL), _dom(NULL), file(NULL), _vertexBlendWeights(NULL), _vertexBlendIndices(NULL)
-{
-}
-
-DAESceneEncoder::~DAESceneEncoder()
-{
-}
-
-unsigned int getMaxOffset(domInputLocalOffset_Array& inputArray)
-{
-    unsigned int maxOffset = 0;
-    for (unsigned int i = 0; i < (int)inputArray.getCount(); ++i)
-    {
-        if ( inputArray[i]->getOffset() > maxOffset )
-        {
-            maxOffset = (unsigned int)inputArray[i]->getOffset();
-        }
-    }
-
-    return maxOffset;
-}
-
-void DAESceneEncoder::optimizeCOLLADA(const EncoderArguments& arguments, domCOLLADA* dom)
-{
-    const std::vector<std::string>& groupAnimatioNodeIds = arguments.getGroupAnimationNodeId();
-    const std::vector<std::string>& groupAnimatioIds = arguments.getGroupAnimationAnimationId();
-    assert(groupAnimatioNodeIds.size() == groupAnimatioIds.size());
-    if (!groupAnimatioNodeIds.empty())
-    {
-        size_t size = groupAnimatioNodeIds.size();
-        if (size > 0)
-        {
-            DAEOptimizer optimizer(dom);
-            begin();
-            for (size_t i = 0; i < size; ++i)
-            {
-                optimizer.combineAnimations(groupAnimatioNodeIds[i], groupAnimatioIds[i]);
-            }
-            end("groupAnimation");
-        }
-    }
-    else
-    {
-        // Determine if there are any mesh skins that are animated that have more than 1 animation targeting its joints.
-        // (candidates for grouping)
-        std::vector<std::string> nodeIds;
-        if (findGroupAnimationNodes(dom, nodeIds))
-        {
-            // Ask the user if they want to group animations automatically.
-            if (promptUserGroupAnimations())
-            {
-                LOG(2, "Grouping animations...\n");
-
-                DAEOptimizer optimizer(dom);
-                begin();
-                char buffer[20];
-                size_t size = nodeIds.size();
-                for (size_t i = 0; i < size; ++i)
-                {
-                    // In COLLADA, ids must be unique but they don't have to be unique in GPB.
-                    // Save the animation id as "animations___#" and then rename it once the GPB objects are created
-                    // but before the GPB is written to file.
-                    sprintf(buffer, "animations___%lu", i);
-                    std::string animationId(buffer);
-                    _tempGroupAnimationIds.push_back(animationId);
-                    optimizer.combineAnimations(nodeIds[i], animationId);
-                }
-                end("groupAnimation");
-            }
-        }
-    }
-    if (arguments.DAEOutputEnabled())
-    {
-        if (!_collada->writeTo(arguments.getFilePath(), arguments.getDAEOutputPath()))
-        {
-            LOG(1, "Error: COLLADA failed to write the dom for file: %s\n", arguments.getDAEOutputPath().c_str());
-        }
-    }
-}
-
-void DAESceneEncoder::triangulate(DAE* dae)
-{
-    daeDatabase* dataBase = dae->getDatabase();
-
-    int geometryCount = (int)(dataBase->getElementCount(0, "geometry"));
-    for (int i = 0; i < geometryCount; ++i)
-    {
-        // Find the next geometry element.
-        domGeometry* domGeometry;
-        dataBase->getElement((daeElement**)&domGeometry, i, 0, "geometry");
-
-        // Get the mesh out of the geometry.
-        const domMeshRef domMesh = domGeometry->getMesh();
-        if (!domMesh)
-        {
-            continue;
-        }
-
-        // Loop over all the polygons elements.
-        int polygonsCount = (int)(domMesh->getPolygons_array().getCount());
-        for (int j = 0; j < polygonsCount; ++j)
-        {
-            // Get the polygons out of the mesh.
-            domPolygons* domPolygons = domMesh->getPolygons_array()[j];
-            // Create the triangles from the polygons
-            createTrianglesFromPolygons(domMesh, domPolygons);
-        }
-        while (domMesh->getPolygons_array().getCount() > 0)
-        {
-            domPolygons* domPolygons = domMesh->getPolygons_array().get(0);
-            // Remove the polygons from the mesh.
-            domMesh->removeChildElement(domPolygons);
-        }
-
-        // Loop over all the polylist elements.
-        int polylistCount = (int)(domMesh->getPolylist_array().getCount());
-        for (int j = 0; j < polylistCount; ++j)
-        {
-            // Get the polylist out of the mesh.
-            domPolylist* domPolylist = domMesh->getPolylist_array()[j];
-            // Create the triangles from the polygon list
-            createTrianglesFromPolylist(domMesh, domPolylist);
-        }
-        while (domMesh->getPolylist_array().getCount() > 0)
-        {
-            domPolylist* domPolylist = domMesh->getPolylist_array().get(0);
-            // Remove the polylist from the mesh.
-            domMesh->removeChildElement(domPolylist);
-        }
-    }
-}
-
-void DAESceneEncoder::createTrianglesFromPolygons(domMesh* domMesh, domPolygons* domPolygons)
-{
-    // Create a new <triangles> inside the mesh that has the same material as the <polygons>.
-    domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles");
-    triangles->setCount(0);
-    triangles->setMaterial(domPolygons->getMaterial());
-    domP* domTrianglesP = (domP*)triangles->createAndPlace("p");
-    
-    // Give the new <triangles> the same <_dae> and <parameters> as the old  <polygons>.
-    for (unsigned int i = 0; i < domPolygons->getInput_array().getCount(); ++i)
-    {
-        triangles->placeElement(domPolygons->getInput_array()[i]->clone());
-    }
-    
-    // Get the number of inputs and primitives for the polygons array.
-    unsigned int inputCount = getMaxOffset(domPolygons->getInput_array()) + 1;
-    unsigned int primitiveCount = domPolygons->getP_array().getCount();
-    
-    // Triangulate all the primitives, this generates all the triangles in a single <p> element.
-    for (unsigned int j = 0; j < primitiveCount; ++j)
-    {
-        // Check the polygons for consistancy (some exported files have had the wrong number of indices).
-        domP* domCurrentP = domPolygons->getP_array()[j];
-        int elementCount = (int)(domCurrentP->getValue().getCount());
-        if ( (elementCount % inputCount) != 0 )
-        {
-            // Skip this case.
-        }
-        else
-        {
-            unsigned int triangleCount = (elementCount / inputCount) - 2;
-            
-            // Write out the primitives as triangles, just fan using the first element as the base.
-            unsigned int index = inputCount;
-            for (unsigned int k = 0; k < triangleCount; ++k)
-            {
-                // First vertex.
-                for (unsigned int l = 0; l < inputCount; ++l)
-                {
-                    domTrianglesP->getValue().append(domCurrentP->getValue()[l]);
-                }
-                // Second vertex.
-                for (unsigned int l = 0; l < inputCount; ++l)
-                {
-                    domTrianglesP->getValue().append(domCurrentP->getValue()[index + l]);
-                }
-                // Third vertex.
-                index += inputCount;
-                for (unsigned int l = 0; l < inputCount; ++l)
-                {
-                    domTrianglesP->getValue().append(domCurrentP->getValue()[index + l]);
-                }
-                triangles->setCount(triangles->getCount() + 1);
-            }
-        }
-    }
-}
-
-void DAESceneEncoder::createTrianglesFromPolylist(domMesh* domMesh, domPolylist* domPolylist)
-{
-    // Create a new <triangles> inside the mesh that has the same material as the <polylist>.
-    domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles");
-    triangles->setMaterial(domPolylist->getMaterial());
-    domP* domTrianglesP = (domP*)triangles->createAndPlace("p");
-    
-    // Give the new <triangles> the same <_dae> and <parameters> as the old <polylist>.
-    for (int i = 0; i < (int)(domPolylist->getInput_array().getCount()); ++i)
-    {
-        triangles->placeElement(domPolylist->getInput_array()[i]->clone());
-    }
-    
-    // Get the number of inputs and primitives for the polygons array.
-    unsigned int inputCount = getMaxOffset(domPolylist->getInput_array()) + 1;
-    unsigned int primitiveCount = domPolylist->getVcount()->getValue().getCount();
-    
-    unsigned int offset = 0;
-    unsigned int trianglesProcessed = 0;
-    
-    // Triangulate all the primitives, this generates all the triangles in a single <p> element.
-    for (unsigned int j = 0; j < primitiveCount; ++j)
-    {
-        unsigned int triangleCount = (unsigned int)domPolylist->getVcount()->getValue()[j] - 2;
-        
-        // Write out the primitives as triangles, just fan using the first element as the base.
-        int index = inputCount;
-        for (unsigned int k = 0; k < triangleCount; ++k)
-        {
-            // First vertex.
-            for (unsigned int l = 0; l < inputCount; ++l)
-            {
-                domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + l]);
-            }
-            // Second vertex.
-            for (unsigned int l = 0; l < inputCount; ++l)
-            {
-                domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]);
-            }
-            // Third vertex.
-            index += inputCount;
-            for (unsigned int l = 0; l < inputCount; ++l)
-            {
-                domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]);
-            }
-            
-            trianglesProcessed++;
-        }
-        
-        offset += (unsigned int)domPolylist->getVcount()->getValue()[j] * inputCount;
-    }
-    triangles->setCount(trianglesProcessed);
-}
-
-void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments& arguments)
-{
-    _begin = clock();
-    const char* nodeId = arguments.getNodeId();
-    
-    // Load the collada document
-    _collada = new DAE();
-    begin();
-    _dom = (domCOLLADA*)_collada->open(filepath);
-    end("Open file");
-    if (!_dom)
-    {
-        LOG(1, "Error: COLLADA failed to open file: %s\n", filepath.c_str());
-        if (_collada)
-        {
-            delete _collada;
-            _collada = NULL;
-        }
-        return;
-    }
-    
-    // Run collada conditioners
-    begin();
-    triangulate(_collada);
-    end("triangulate");
-
-    // Optimize the dom before encoding
-    optimizeCOLLADA(arguments, _dom);
-
-    // Find the <visual_scene> element within the <scene>
-    const domCOLLADA::domSceneRef& domScene = _dom->getScene();
-    if (domScene && domScene->getInstance_visual_scene())
-    {
-        daeElement* scene = getVisualScene(domScene);
-        if (scene)
-        {
-            if (nodeId == NULL)
-            {
-                // If the -i <node_id> parameter was not passed then write out the entire scene.
-                begin();
-                loadScene((domVisual_scene*)scene);
-                end("load scene");
-            }
-            else
-            {
-                // Resolve/Search for the node the user specified with the -i <node_id> parameter.
-                daeSIDResolver resolver(scene, nodeId);
-                domNode* nodeElement = daeSafeCast<domNode>(resolver.getElement());
-                if (nodeElement)
-                {
-                    Node* node = loadNode(nodeElement, NULL);
-                    if (node)
-                    {
-                        _gamePlayFile.addScenelessNode(node);
-                    }
-                    else
-                    {
-                        LOG(1, "COLLADA File loaded to the dom, but failed to load node %s.\n", nodeId);
-                    }
-                }
-                else
-                {
-                    LOG(1, "COLLADA File loaded to the dom, but node was not found with node ID %s.\n", nodeId);
-                }
-            }
-        }
-        else
-        {
-             LOG(1, "COLLADA File loaded to the dom, but query for the dom assets failed.\n");
-        }
-    }
-    else
-    {
-        LOG(1, "COLLADA File loaded to the dom, but missing <visual_scene>.\n");
-    }
-    
-    // The animations should be loaded last
-    begin();
-    loadAnimations(_dom);
-    end("loadAnimations");
-
-    _gamePlayFile.adjust();
-    _gamePlayFile.renameAnimations(_tempGroupAnimationIds, "animations");
-
-    // Write the output file
-    std::string outputFilePath = arguments.getOutputFilePath();
-    if (arguments.textOutputEnabled())
-    {
-        int pos = outputFilePath.find_last_of('.');
-        if (pos > 2)
-        {
-            std::string path = outputFilePath.substr(0, pos);
-            path.append(".xml");
-            LOG(1, "Saving debug file: %s\n", path.c_str());
-            if (!_gamePlayFile.saveText(path))
-            {
-                LOG(1, "Error writing text file: %s\n", path.c_str());
-            }
-        }
-    }
-    else
-    {
-        LOG(1, "Saving binary file: %s\n", outputFilePath.c_str());
-        begin();
-        if (!_gamePlayFile.saveBinary(outputFilePath))
-        {
-            LOG(1, "Error writing binary file: %s\n", outputFilePath.c_str());
-        }
-        end("save binary");
-    }
-    
-    // Cleanup
-    if (file)
-    {
-        fclose(file);
-    }
-    if (_collada)
-    {
-        delete _collada;
-        _collada = NULL;
-    }
-}
-
-void DAESceneEncoder::loadAnimations(const domCOLLADA* dom)
-{
-    // Call loadAnimation on all <animation> elements in all <library_animations>
-    const domLibrary_animations_Array& animationLibrarys = dom->getLibrary_animations_array();
-    size_t animationLibrarysCount = animationLibrarys.getCount();
-    for (size_t i = 0; i < animationLibrarysCount; ++i)
-    {
-        const domLibrary_animationsRef& libraryAnimation = animationLibrarys.get(i);
-        const domAnimation_Array& animationArray = libraryAnimation->getAnimation_array();
-        size_t animationCount = animationArray.getCount();
-        for (size_t j = 0; j < animationCount; ++j)
-        {
-            const domAnimationRef& animationRef = animationArray.get(j);
-            loadAnimation(animationRef);
-        }
-    }
-}
-
-void DAESceneEncoder::loadAnimation(const domAnimationRef animationRef, const char* altId)
-{
-    // Animations can contain other animations.
-    const domAnimation_Array& animationArray = animationRef->getAnimation_array();
-    unsigned int animationCount = animationArray.getCount();
-    
-    if (animationCount == 1)
-    {
-        // DAE_FBX nests 1 animation within another animation for some reason.
-        loadAnimation(animationArray.get(0), animationRef->getId());
-    }
-    else if ( animationCount > 1)
-    {
-        loadAnimation(animationArray.get(0));
-    }
-
-    // <channel> points to one <sampler>
-    // <sampler> points to multiple <input> elements
-
-    // <channel>
-    const domChannel_Array& channelArray = animationRef->getChannel_array();
-    size_t channelArrayCount = channelArray.getCount();
-    if (channelArrayCount > 0)
-    {
-        Animation* animation = new Animation();
-        const char* str = animationRef->getId();
-        if (str)
-        {
-            animation->setId(str);
-        }
-        else if (altId)
-        {
-            animation->setId(altId);
-        }
-
-        for (size_t i = 0; i < channelArrayCount; ++i)
-        {
-            AnimationChannel* animationChannel = new AnimationChannel();
-
-            const domChannelRef& channelRef = channelArray.get(i);
-
-            // <sampler>
-            const domSamplerRef sampler = getSampler(channelRef);
-            assert(sampler);
-
-            // <input>
-            const domInputLocal_Array& inputArray = sampler->getInput_array();
-            size_t inputArrayCount = inputArray.getCount();
-            for (size_t j = 0; j < inputArrayCount; ++j)
-            {
-                const domInputLocalRef& inputLocal = inputArray.get(j);
-
-                // <source>
-                const domSourceRef source = getSource(inputLocal, animationRef);
-
-                std::string semantic = inputLocal->getSemantic();
-                if (equals(semantic, "INTERPOLATION"))
-                {
-                    // Interpolation source is a list of strings
-                    loadInterpolation(source, animationChannel);
-                }
-                else
-                {
-                    // The other sources are lists of floats.
-                    std::vector<float> floats;
-                    copyFloats(source->getFloat_array(), &floats);
-                    if (equals(semantic, "INPUT"))
-                    {
-                        // TODO: Ensure param name is TIME?
-                        for (std::vector<float>::iterator k = floats.begin(); k != floats.end(); ++k)
-                        {
-                            // Convert seconds to milliseconds
-                            *k = *k * 1000.0f;
-                        }
-                        animationChannel->setKeyTimes(floats);
-                    }
-                    else if (equals(semantic, "OUTPUT"))
-                    {
-                        animationChannel->setKeyValues(floats);
-                    }
-                    else if (equals(semantic, "IN_TANGENT"))
-                    {
-                        animationChannel->setTangentsIn(floats);
-                    }
-                    else if (equals(semantic, "OUT_TANGENT"))
-                    {
-                        animationChannel->setTangentsOut(floats);
-                    }
-                }
-            }
-            
-            // get target attribute enum value
-            if (loadTarget(channelRef, animationChannel))
-            {
-                if (animationChannel->getKeyTimes().size() > 0)
-                {
-                    animation->add(animationChannel);
-                }
-            }
-        }
-        if (animation->getAnimationChannelCount() > 0)
-        {
-            _gamePlayFile.addAnimation(animation);
-        }
-        else
-        {
-            delete animation;
-        }
-    }
-}
-
-void DAESceneEncoder::loadInterpolation(const domSourceRef source, AnimationChannel* animationChannel)
-{
-    // COLLADA stores the interpolations as a list of strings while GBP uses unsigned int
-    std::vector<unsigned int> values;
-    const domName_arrayRef nameArray = getSourceNameArray(source);
-    assert(nameArray);
-    const domListOfNames& names = nameArray->getValue();
-    size_t count = (size_t)names.getCount();
-    values.resize(count);
-    if (count > 0)
-    {
-        for (size_t i = 0; i < count; ++i)
-        {
-            values[i] = AnimationChannel::getInterpolationType(names.get(i));
-        }
-
-        // If all of the interpolation types are the same then only store the interpolation once
-        // instead of storing the same type for each key frame.
-        unsigned int firstType = values[0];
-        bool allEqual = true;
-        for (size_t i = 1; i < count; ++i)
-        {
-            if (firstType != values[i])
-            {
-                allEqual = false;
-                break;
-            }
-        }
-        if (allEqual)
-        {
-            values.resize(1);
-        }
-    }
-    animationChannel->setInterpolations(values);
-}
-
-bool DAESceneEncoder::loadTarget(const domChannelRef& channelRef, AnimationChannel* animationChannel)
-{
-    // GamePlay requires that animations are baked. Use "Bake Transforms" in your 3D modeling tool.
-    // If the target of an animation is not a matrix then an error will be printed.
-
-    const static char* TRANSFORM_WARNING_FORMAT = "Warning: Node \"%s\":\n %s %s\n";
-    const static char* TRANSFORM_MESSAGE = "transform found but not supported.\n Use \"Bake Transforms\" option when exporting.";
-
-    unsigned int targetProperty = 0;
-    DAEChannelTarget channelTarget(channelRef);
-
-    const char* targetId = channelTarget.getTargetId().c_str();
-
-    // TODO: Do we want to support more than one? If yes then this needs to be fixed.
-    for (size_t i = 0; i < channelTarget.getTargetAttributeCount(); ++i)
-    {
-        std::string prop;
-        channelTarget.getPropertyName(i, &prop);
-        daeElement* attributeElement = channelTarget.getTargetAttribute(i);
-        if (attributeElement)
-        {
-            daeInt type = attributeElement->typeID();
-            if (type == domRotate::ID())
-            {
-                LOG(1, TRANSFORM_WARNING_FORMAT, targetId, "Rotate", TRANSFORM_MESSAGE);
-                return false;
-                /*
-                // <rotate>
-                const domRotate* rotate = daeSafeCast<domRotate>(attributeElement);
-
-                if (prop.size() > 0)
-                {
-                    if (equalsIgnoreCase(prop, "ANGLE"))
-                    {
-                        targetProperty = Transform::ANIMATE_ROTATE;
-
-                        // get the rotation axis
-                        const domFloat4& f = rotate->getValue();
-                        float x = (float)f.get(0);
-                        float y = (float)f.get(1);
-                        float z = (float)f.get(2);
-                        
-                        // Get the angle values that were already read
-                        const std::vector<float>& keyValues = animationChannel->getKeyValues();
-                        size_t size = keyValues.size();
-                        assert(size > 0);
-                        // COLLADA only targeted a single prop but GBP requires all 4 rotate values.
-                        // Convert (ANGLE ANGLE ANGLE) to (X Y Z ANGLE X Y Z ANGLE X Y Z ANGLE)
-                        std::vector<float> floats(size * 4);
-                        // Duplicate rotation axis. We will replace only the angle that COLLADA is targeting.
-                        for (size_t j = 0; j < size; ++j)
-                        {
-                            size_t k = j * 4;
-                            floats[k+0] = x;
-                            floats[k+1] = y;
-                            floats[k+2] = z;
-                            floats[k+3] = keyValues[j]; // angle
-                        }
-                        animationChannel->setKeyValues(floats);
-                    }
-                }
-                */
-            }
-            else if (type == domScale::ID())
-            {
-                LOG(1, TRANSFORM_WARNING_FORMAT, targetId, "Scale", TRANSFORM_MESSAGE);
-                return false;
-                /*
-                // <scale>
-                //const domScale* scale = daeSafeCast<domScale>(attributeElement);
-                if (equalsIgnoreCase(prop, "X"))
-                {
-                    targetProperty = Transform::ANIMATE_SCALE_X;
-                }
-                else if (equalsIgnoreCase(prop, "Y"))
-                {
-                    targetProperty = Transform::ANIMATE_SCALE_Y;
-                }
-                else if (equalsIgnoreCase(prop, "Z"))
-                {
-                    targetProperty = Transform::ANIMATE_SCALE_Z;
-                }
-                else
-                {
-                    targetProperty = Transform::ANIMATE_SCALE;
-                }
-                */
-            }
-            else if (type == domTranslate::ID())
-            {
-                LOG(1, TRANSFORM_WARNING_FORMAT, targetId, "Translate", TRANSFORM_MESSAGE);
-                return false;
-                /*
-                // <translate>
-                //const domTranslate* translate = daeSafeCast<domTranslate>(attributeElement);
-                if (equalsIgnoreCase(prop, "X"))
-                {
-                    targetProperty = Transform::ANIMATE_TRANSLATE_X;
-                }
-                else if (equalsIgnoreCase(prop, "Y"))
-                {
-                    targetProperty = Transform::ANIMATE_TRANSLATE_Y;
-                }
-                else if (equalsIgnoreCase(prop, "Z"))
-                {
-                    targetProperty = Transform::ANIMATE_TRANSLATE_Z;
-                }
-                else
-                {
-                    targetProperty = Transform::ANIMATE_TRANSLATE;
-                }
-                */
-            }
-            else if (type == domMatrix::ID())
-            {
-                // If the animation is targeting a matrix then convert it into
-                // a scale, rotate, translate animation by decomposing the matrix.
-                targetProperty = Transform::ANIMATE_SCALE_ROTATE_TRANSLATE;
-
-                const std::vector<float>& keyValues = animationChannel->getKeyValues();
-                assert(keyValues.size() % 16 == 0);
-                // The matrix was 16 floats and the new values will be 10 floats
-                size_t newSize = keyValues.size() / 16 * 10;
-                std::vector<float> floats(newSize);
-
-                size_t matrixCount = keyValues.size() / 16;
-                for (size_t i = 0; i < matrixCount; ++i)
-                {
-                    size_t j = i * 16;
-                    // COLLADA used row-major but the Matrix class uses column-major
-                    Matrix matrix(
-                        keyValues[j+0], keyValues[j+4], keyValues[j+8], keyValues[j+12],
-                        keyValues[j+1], keyValues[j+5], keyValues[j+9], keyValues[j+13],
-                        keyValues[j+2], keyValues[j+6], keyValues[j+10], keyValues[j+14],
-                        keyValues[j+3], keyValues[j+7], keyValues[j+11], keyValues[j+15]);
-                    Vector3 scale;
-                    Quaternion rotation;
-                    Vector3 translation;
-                    matrix.decompose(&scale, &rotation, &translation);
-                    rotation.normalize();
-
-                    size_t k = i * 10;
-                    floats[k+0] = scale.x;
-                    floats[k+1] = scale.y;
-                    floats[k+2] = scale.z;
-                    floats[k+3] = rotation.x;
-                    floats[k+4] = rotation.y;
-                    floats[k+5] = rotation.z;
-                    floats[k+6] = rotation.w;
-                    floats[k+7] = translation.x;
-                    floats[k+8] = translation.y;
-                    floats[k+9] = translation.z;
-                }
-                animationChannel->setKeyValues(floats);
-            }
-        }
-    }
-    animationChannel->setTargetAttribute(targetProperty);
-    animationChannel->setTargetId(channelTarget.getTargetId());
-    //animationChannel->removeDuplicates();
-    return true;
-}
-
-void DAESceneEncoder::begin()
-{
-    #ifdef ENCODER_PRINT_TIME
-    _begin = clock();
-    #endif
-}
-
-void DAESceneEncoder::end(const char* str)
-{
-    #ifdef ENCODER_PRINT_TIME
-    clock_t time = clock() - _begin;
-    LOG(1, "%5d %s\n", time, str);
-    #endif
-}
-
-void DAESceneEncoder::copyFloats(const domFloat_array* source, std::vector<float>* target)
-{
-    std::vector<float>& t = *target;
-
-    size_t count = (size_t)source->getCount();
-    t.resize(count);
-    const domListOfFloats& listOfFloats = source->getValue();
-    for (size_t i = 0; i < count; ++i)
-    {
-        t[i] = (float)listOfFloats.get(i);
-    }
-}
-
-void DAESceneEncoder::loadScene(const domVisual_scene* visualScene)
-{
-    Scene* scene = new Scene();
-
-    const domNode_Array& nodes = visualScene->getNode_array();
-    scene->setId(visualScene->getId());
-    if (scene->getId().length() == 0)
-    {
-        scene->setId("__SCENE__");
-    }
-
-    size_t childCount = nodes.getCount();
-    for (size_t i = 0; i < childCount; ++i)
-    {
-        scene->add(loadNode(nodes[i], NULL));
-    }
-    
-    Node* activeCameraNode = findSceneActiveCameraNode(visualScene, scene);
-    if (activeCameraNode)
-    {
-        scene->setActiveCameraNode(activeCameraNode);
-    }
-
-    _gamePlayFile.addScene(scene);
-}
-
-Node* DAESceneEncoder::findSceneActiveCameraNode(const domVisual_scene* visualScene, Scene* scene)
-{
-    // Loops through each evaluate_scene's render until an active camera node is found.
-    // Returns the first one found.
-
-    // Find the active camera
-    const domVisual_scene::domEvaluate_scene_Array& evaluateScenes = visualScene->getEvaluate_scene_array();
-    size_t evaluateSceneCount = evaluateScenes.getCount();
-    for (size_t i = 0; i < evaluateSceneCount; ++i)
-    {
-        const domVisual_scene::domEvaluate_scene::domRender_Array& renders = evaluateScenes[i]->getRender_array();
-        size_t renderCount = renders.getCount();
-        for (size_t j = 0; j < renderCount; ++j)
-        {
-            xsAnyURI cameraNodeURI = renders[i]->getCamera_node();
-            domNode* nodeRef = daeSafeCast<domNode>(cameraNodeURI.getElement());
-            if (nodeRef)
-            {
-                std::string id = nodeRef->getId();
-                Node* node = _gamePlayFile.getNode(id.c_str());
-                if (node)
-                {
-                    return node;
-                }
-            }
-        }
-    }
-    // Find the first node in the scene that contains a camera.
-    return scene->getFirstCameraNode();
-}
-
-Node* DAESceneEncoder::loadNode(domNode* n, Node* parent)
-{
-    Node* node = NULL;
-
-    // Check if this node has already been loaded
-    const char* id = n->getID();
-    if (id && strlen(id) > 0)
-    {
-        node = _gamePlayFile.getNode(n->getID());
-        if (node)
-        {
-            return node;
-        }
-    }
-    
-    // Load the node
-    node = new Node();
-
-    if (parent)
-    {
-        parent->addChild(node);
-    }
-    
-    if (n->getType() == NODETYPE_JOINT)
-    {
-        node->setIsJoint(true);
-    }
-
-    // Set node id
-    node->setId(n->getId());
-
-    // If this node has an id then add it to the ref table
-    _gamePlayFile.addNode(node);
-
-    transformNode(n, node);
-    loadControllerInstance(n, node);
-    loadCameraInstance(n, node);
-    loadLightInstance(n, node);
-    loadGeometryInstance(n, node);
-    
-    // Load child nodes
-    const domNode_Array& childNodes = n->getNode_array();
-    size_t childCount = childNodes.getCount();
-    for (size_t i = 0; i < childCount; ++i)
-    {
-        loadNode(childNodes.get(i), node);
-    }
-    return node;
-}
-
-void DAESceneEncoder::transformNode(domNode* domNode, Node* node)
-{
-    // Apply the transform.
-    // Note that we only honor the first matrix transform specified for the DOM node.
-    const domMatrix_Array& matrixArray = domNode->getMatrix_array();
-    if (matrixArray.getCount() > 0)
-    {
-        const domMatrixRef& matrix = matrixArray.get(0);
-        if (!matrix)
-        {
-            return;
-        }
-        const domFloat4x4& tx = matrix->getValue();
-        float transform[] = {(float)tx.get(0), (float)tx.get(4), (float)tx.get(8), (float)tx.get(12),
-                              (float)tx.get(1), (float)tx.get(5), (float)tx.get(9), (float)tx.get(13),
-                              (float)tx.get(2), (float)tx.get(6), (float)tx.get(10), (float)tx.get(14),
-                              (float)tx.get(3), (float)tx.get(7), (float)tx.get(11), (float)tx.get(15)};
-        node->setTransformMatrix(transform);
-    }
-    else
-    {
-        Matrix transform;
-        calcTransform(domNode, transform);
-        node->setTransformMatrix(transform.m);
-    }
-
-    // TODO: Handle transforming by other types (SRT, etc) (see "Node" child elements spec)
-
-    /*Vector3 scale;
-    Quaternion rotation;
-    Vector3 translation;
-
-    localTransform.Decompose(&scale, &rotation, &translation);
-
-    node->SetScale(scale);
-    node->SetRotation(rotation);
-    node->SetTranslation(translation);*/
-}
-
-void DAESceneEncoder::calcTransform(domNode* domNode, Matrix& dstTransform)
-{
-    daeTArray<daeSmartRef<daeElement> > children;
-    domNode->getChildren(children);
-    size_t childCount = children.getCount();
-    for (size_t i = 0; i < childCount; ++i)
-    {
-        daeElementRef childElement = children[i];
-        daeInt typeID = childElement->typeID();
-        if (typeID == domTranslate::ID())
-        {
-            domTranslateRef translateNode = daeSafeCast<domTranslate>(childElement);
-            float x = (float)translateNode->getValue().get(0);
-            float y = (float)translateNode->getValue().get(1);
-            float z = (float)translateNode->getValue().get(2);
-            dstTransform.translate(x, y, z);
-        }
-        if (typeID == domRotate::ID())
-        {
-            domRotateRef rotateNode = daeSafeCast<domRotate>(childElement);
-            float x = (float)rotateNode->getValue().get(0);
-            float y = (float)rotateNode->getValue().get(1);
-            float z = (float)rotateNode->getValue().get(2);
-            float angle = MATH_DEG_TO_RAD((float)rotateNode->getValue().get(3)); // COLLADA uses degrees, gameplay uses radians
-            if (x == 1.0f && y == 0.0f && z == 0.0f)
-            {
-                dstTransform.rotateX(angle);
-            }
-            else if (x == 0.0f && y == 1.0f && z == 0.0f)
-            {
-                dstTransform.rotateY(angle);
-            }
-            else if (x == 0.0f && y == 0.0f && z == 1.0f)
-            {
-                dstTransform.rotateZ(angle);
-            }
-            else
-            {
-                dstTransform.rotate(x, y, z, angle);
-            }
-        }
-        if (typeID == domScale::ID())
-        {
-            domScaleRef scaleNode = daeSafeCast<domScale>(childElement);
-            float x = (float)scaleNode->getValue().get(0);
-            float y = (float)scaleNode->getValue().get(1);
-            float z = (float)scaleNode->getValue().get(2);
-            dstTransform.scale(x, y, z);
-        }
-        if (typeID == domSkew::ID())
-        {
-            LOG(1, "Warning: Skew transform found but not supported.\n");
-        }
-        if (typeID == domLookat::ID())
-        {
-            LOG(1, "Warning: Lookat transform found but not supported.\n");
-        }
-    }
-}
-
-void DAESceneEncoder::loadCameraInstance(const domNode* n, Node* node)
-{
-    // Does this node have any camera instances?
-    const domInstance_camera_Array& instanceCameras = n->getInstance_camera_array();
-    size_t instanceCameraCount = instanceCameras.getCount();
-    for (size_t i = 0; i < instanceCameraCount; ++i)
-    {
-        // Get the camrea object
-        const domInstance_camera* cameraInstanceRef = instanceCameras.get(i);
-        xsAnyURI cameraURI = cameraInstanceRef->getUrl();
-        domCamera* cameraRef = daeSafeCast<domCamera>(cameraURI.getElement());
-
-        if (cameraRef)
-        {
-            Camera* camera = loadCamera(cameraRef);
-            if (camera)
-            {
-                node->setCamera(camera);
-            }
-        }
-        else
-        {
-            // warning
-        }
-    }
-}
-
-void DAESceneEncoder::loadLightInstance(const domNode* n, Node* node)
-{
-    // Does this node have any light instances?
-    const domInstance_light_Array& instanceLights = n->getInstance_light_array();
-    size_t instanceLightCount = instanceLights.getCount();
-    for (size_t i = 0; i < instanceLightCount; ++i)
-    {
-        // Get the camrea object
-        const domInstance_light* lightInstanceRef = instanceLights.get(i);
-        xsAnyURI lightURI = lightInstanceRef->getUrl();
-        domLight* lightRef = daeSafeCast<domLight>(lightURI.getElement());
-
-        if (lightRef)
-        {
-            Light* light = loadLight(lightRef);
-            if (light)
-            {
-                node->setLight(light);
-            }
-        }
-        else
-        {
-            // warning
-        }
-    }
-}
-
-void DAESceneEncoder::loadGeometryInstance(const domNode* n, Node* node)
-{
-    // Does this node have any geometry instances?
-    const domInstance_geometry_Array& instanceGeometries = n->getInstance_geometry_array();
-    size_t instanceGeometryCount = instanceGeometries.getCount();
-    for (size_t i = 0; i < instanceGeometryCount; ++i)
-    {
-        // Get the geometry object
-        const domInstance_geometryRef geometryInstanceRef = instanceGeometries.get(i);
-        xsAnyURI geometryURI = geometryInstanceRef->getUrl();
-        domGeometry* geometry = daeSafeCast<domGeometry>(geometryURI.getElement());
-
-        // Load the model from this geometry
-        if (geometry)
-        {
-            Model* model = loadGeometry(geometry, geometryInstanceRef->getBind_material());
-            if (model)
-            {
-                node->setModel(model);
-            }
-        }
-        else
-        {
-            LOG(1, "Failed to resolve geometry url: %s\n", geometryURI.getURI());
-        }
-    }
-}
-
-void DAESceneEncoder::loadControllerInstance(const domNode* n, Node* node)
-{
-    // Does this node have any controller instances?
-    const domInstance_controller_Array& instanceControllers = n->getInstance_controller_array();
-    size_t instanceControllerCount = instanceControllers.getCount();
-    for (size_t i = 0; i < instanceControllerCount; ++i)
-    {
-        const domInstance_controllerRef instanceControllerRef = instanceControllers.get(i);
-        xsAnyURI controllerURI = instanceControllerRef->getUrl();
-        domController* controllerRef = daeSafeCast<domController>(controllerURI.getElement());
-
-        if (controllerRef)
-        {
-            const domSkin* skinElement = controllerRef->getSkin();
-            if (skinElement)
-            {
-                Model* model = loadSkin(skinElement);
-                if (model)
-                {
-                    domInstance_controller::domSkeleton_Array& skeletons = instanceControllerRef->getSkeleton_array();
-                    if (skeletons.getCount() == 0)
-                    {
-                        domNode* rootJoint = getRootJointNode(skinElement);
-                        if (rootJoint)
-                        {
-                            loadSkeleton(rootJoint, model->getSkin());
-                            node->setModel(model);
-                        }
-                    }
-                    else
-                    {
-                        // Load the skeleton for this skin
-                        domInstance_controller::domSkeletonRef skeleton = getSkeleton(instanceControllerRef);
-                        assert(skeleton);
-                        loadSkeleton(skeleton, model->getSkin());
-                        node->setModel(model);
-                    }
-                }
-            }
-        }
-        else
-        {
-            // warning
-        }
-        _jointLookupTable.clear();
-        _jointInverseBindPoseMatrices.clear();
-    }
-}
-
-Camera* DAESceneEncoder::loadCamera(const domCamera* cameraRef)
-{
-    Camera* camera = new Camera();
-    camera->setId(cameraRef->getId());
-
-    // Optics
-    const domCamera::domOpticsRef opticsRef = cameraRef->getOptics();
-    if (opticsRef.cast())
-    {
-        const domCamera::domOptics::domTechnique_commonRef techRef = opticsRef->getTechnique_common();
-
-        // Orthographics
-        const domCamera::domOptics::domTechnique_common::domOrthographicRef orthographicRef = techRef->getOrthographic();
-        if (orthographicRef.cast())
-        {
-            camera->setOrthographic();
-            camera->setAspectRatio((float)orthographicRef->getAspect_ratio()->getValue());
-            camera->setNearPlane((float)orthographicRef->getZnear()->getValue());
-            camera->setFarPlane((float)orthographicRef->getZfar()->getValue());
-
-            const domTargetableFloatRef xmag = orthographicRef->getXmag();
-            const domTargetableFloatRef ymag = orthographicRef->getYmag();
-            // Viewport width
-            if (xmag.cast())
-            {
-                camera->setViewportWidth((float)xmag->getValue());
-            }
-            // Viewport height
-            if (ymag.cast())
-            {
-                camera->setViewportHeight((float)ymag->getValue());
-            }
-            // TODO: Viewport x and y?
-        }
-
-        // Perspective
-        const domCamera::domOptics::domTechnique_common::domPerspectiveRef perspectiveRef = techRef->getPerspective();
-        if (perspectiveRef.cast())
-        {
-            camera->setPerspective();
-            camera->setNearPlane((float)perspectiveRef->getZnear()->getValue());
-            camera->setFarPlane((float)perspectiveRef->getZfar()->getValue());
-
-            float aspectRatio = -1.0f;
-            if (perspectiveRef->getAspect_ratio().cast())
-            {
-                aspectRatio = (float)perspectiveRef->getAspect_ratio()->getValue();
-                camera->setAspectRatio(aspectRatio);
-            }
-            if (perspectiveRef->getYfov().cast())
-            {
-                camera->setFieldOfView((float)perspectiveRef->getYfov()->getValue());
-            }
-            else if (perspectiveRef->getXfov().cast() && aspectRatio > 0.0f)
-            {
-                // The gameplaybinary stores the yfov but collada might have specified
-                // an xfov and an aspect ratio. So use those to calculate the yfov.
-                float xfov = (float)perspectiveRef->getXfov()->getValue();
-                float yfov = xfov / aspectRatio;
-                camera->setFieldOfView(yfov);
-            }
-        }
-    }
-    _gamePlayFile.addCamera(camera);
-    return camera;
-}
-
-Light* DAESceneEncoder::loadLight(const domLight* lightRef)
-{
-    Light* light = new Light();
-    light->setId(lightRef->getId());
-
-    const domLight::domTechnique_commonRef techRef = lightRef->getTechnique_common();
-
-    // Ambient light
-    {
-        const domLight::domTechnique_common::domAmbientRef ambientRef = techRef->getAmbient();
-        if (ambientRef.cast())
-        {
-            light->setAmbientLight();
-            // color
-            const domTargetableFloat3Ref float3Ref = ambientRef->getColor();
-            const domFloat3& color3 = float3Ref->getValue();
-            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
-        }
-    }
-
-    // Directional light
-    {
-        const domLight::domTechnique_common::domDirectionalRef direcitonalRef = techRef->getDirectional();
-        if (direcitonalRef.cast())
-        {
-            light->setDirectionalLight();
-            // color
-            const domTargetableFloat3Ref float3Ref = direcitonalRef->getColor();
-            const domFloat3& color3 = float3Ref->getValue();
-            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
-        }
-    }
-
-    // Spot light
-    {
-        const domLight::domTechnique_common::domSpotRef spotRef = techRef->getSpot();
-        if (spotRef.cast())
-        {
-            light->setSpotLight();
-            // color
-            const domTargetableFloat3Ref float3Ref = spotRef->getColor();
-            const domFloat3& color3 = float3Ref->getValue();
-            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
-                
-            const domTargetableFloatRef& constAtt = spotRef->getConstant_attenuation();
-            if (constAtt.cast())
-            {
-                light->setConstantAttenuation((float)constAtt->getValue());
-            }
-
-            const domTargetableFloatRef& linearAtt = spotRef->getLinear_attenuation();
-            if (linearAtt.cast())
-            {
-                light->setLinearAttenuation((float)linearAtt->getValue());
-            }
-
-            const domTargetableFloatRef& quadAtt = spotRef->getQuadratic_attenuation();
-            if (quadAtt.cast())
-            {
-                light->setQuadraticAttenuation((float)quadAtt->getValue());
-            }
-
-            const domTargetableFloatRef& falloffAngle = spotRef->getFalloff_angle();
-            if (falloffAngle.cast())
-            {
-                light->setFalloffAngle((float)falloffAngle->getValue());
-            }
-
-            const domTargetableFloatRef& falloffExp = spotRef->getFalloff_exponent();
-            if (falloffExp.cast())
-            {
-                light->setFalloffExponent((float)falloffExp->getValue());
-            }
-        }
-    }
-
-    // Point light
-    {
-        const domLight::domTechnique_common::domPointRef pointRef = techRef->getPoint();
-        if (pointRef.cast())
-        {
-            light->setPointLight();
-            // color
-            const domTargetableFloat3Ref float3Ref = pointRef->getColor();
-            const domFloat3& color3 = float3Ref->getValue();
-            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
-
-            const domTargetableFloatRef& constAtt = pointRef->getConstant_attenuation();
-            if (constAtt.cast())
-            {
-                light->setConstantAttenuation((float)constAtt->getValue());
-            }
-
-            const domTargetableFloatRef& linearAtt = pointRef->getLinear_attenuation();
-            if (linearAtt.cast())
-            {
-                light->setLinearAttenuation((float)linearAtt->getValue());
-            }
-
-            const domTargetableFloatRef& quadAtt = pointRef->getQuadratic_attenuation();
-            if (quadAtt.cast())
-            {
-                light->setQuadraticAttenuation((float)quadAtt->getValue());
-            }
-
-            // When Maya exports DAE_FBX, the ambient lights are converted into point lights but with not attenuation elements.
-            // If this point light has no attenuation then assume it is ambient.
-            if (!(constAtt.cast() && linearAtt.cast() && quadAtt.cast()))
-            {
-                light->setAmbientLight();
-            }
-        }
-    }
-    _gamePlayFile.addLight(light);
-    return light;
-}
-
-
-void DAESceneEncoder::loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin)
-{
-    xsAnyURI skeletonUri = skeletonElement->getValue();
-    daeString skeletonId = skeletonUri.getID();
-    daeSIDResolver resolver(skeletonUri.getElement(), skeletonId);
-    domNode* rootNode = daeSafeCast<domNode>(resolver.getElement());
-    loadSkeleton(rootNode, skin);
-}
-
-void DAESceneEncoder::loadSkeleton(domNode* rootNode, MeshSkin* skin)
-{
-    // Get the lookup scene id (sid) and joint index.
-    std::string id = std::string(rootNode->getId());
-
-    // Has the skeleton (root joint) been loaded yet?
-    Node* skeleton = (Node*)_gamePlayFile.getFromRefTable(id);
-
-    // The skeleton node is not loaded yet, so let's load it now
-    if (skeleton == NULL)
-    {
-        // Find the top most parent of rootNode that has not yet been loaded
-        domNode* topLevelParent = rootNode;
-        while (
-            topLevelParent->getParent() &&
-            topLevelParent->getParent()->typeID() == domNode::ID() &&
-            _gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID()) == NULL)
-        {
-            topLevelParent = (domNode*)topLevelParent->getParent();
-        }
-
-        // Is the parent of this node loaded yet?
-        Node* parentNode = NULL;
-        if (topLevelParent->getParent() &&
-            topLevelParent->getParent()->typeID() == domNode::ID() &&
-            _gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID()) != NULL)
-        {
-            parentNode = (Node*)_gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID());
-        }
-
-        // Finally, load the node hierarchy that includes the skeleton
-        skeleton = loadNode(topLevelParent, parentNode);
-    }
-
-    if (skeleton == NULL)
-    {
-        // This shouldn't really happen..
-        skeleton = new Node();
-        skeleton->setId(id);
-        _gamePlayFile.addNode(skeleton);
-    }
-
-    // Resolve and set joints array for skin
-    std::vector<Node*> _joints;
-    const std::vector<std::string>& jointNames = skin->getJointNames();
-    for (std::vector<std::string>::const_iterator i = jointNames.begin(); i != jointNames.end(); ++i)
-    {
-        Object* obj = _gamePlayFile.getFromRefTable(*i);
-        if (obj && obj->getTypeId() == Object::NODE_ID)
-        {
-            Node* node = static_cast<Node*>(obj);
-            _joints.push_back(node);
-        }
-    }
-    skin->setJoints(_joints);
-}
-
-Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
-{
-    ///////////////////////////// SKIN
-    Model* model = new Model();
-    MeshSkin* skin = new MeshSkin();
-
-    // Bind Shape Matrix
-    const domSkin::domBind_shape_matrix* bindShapeMatrix = skinElement->getBind_shape_matrix();
-    if (bindShapeMatrix)
-    {
-        const domFloat4x4& m = bindShapeMatrix->getValue();
-        float transform[] = {(float)m.get(0), (float)m.get(4), (float)m.get(8),  (float)m.get(12),
-                             (float)m.get(1), (float)m.get(5), (float)m.get(9),  (float)m.get(13),
-                             (float)m.get(2), (float)m.get(6), (float)m.get(10), (float)m.get(14),
-                             (float)m.get(3), (float)m.get(7), (float)m.get(11), (float)m.get(15)};
-        skin->setBindShape(transform);
-    }
-
-    // Read and set our joints
-    domSkin::domJointsRef _joints = skinElement->getJoints();
-    domInputLocal_Array& jointInputs = _joints->getInput_array();
-
-    // Process "JOINT" input semantic first (we need to do this to set the joint count)
-    unsigned int jointCount = 0;
-    for (unsigned int i = 0; i < jointInputs.getCount(); ++i)
-    {
-        domInputLocalRef input = jointInputs.get(i);
-        std::string inputSemantic = std::string(input->getSemantic());
-        domURIFragmentType* sourceURI = &input->getSource();
-        sourceURI->resolveElement();
-        const domSourceRef source = (domSource*)(daeElement*)sourceURI->getElement();
-
-        if (equals(inputSemantic, "JOINT"))
-        {
-            // Get the joint Ids's
-            std::vector<std::string> list;
-            getJointNames(source, list);
-
-            // Go through the joint list and convert them from sid to id because the sid information is
-            // lost when converting to the gameplay binary format.
-            for (std::vector<std::string>::iterator i = list.begin(); i != list.end(); ++i)
-            {
-                daeSIDResolver resolver(source->getDocument()->getDomRoot(), i->c_str());
-                daeElement* element = resolver.getElement();
-                if (element && element->typeID() == domNode::ID())
-                {
-                    domNodeRef node = daeSafeCast<domNode>(element);
-                    const char* nodeId = node->getId();
-                    if (nodeId && !equals(*i, nodeId))
-                    {
-                        *i = nodeId;
-                    }
-                }
-            }
-
-            // Get the joint count and set the capacities for both the
-            jointCount = list.size();
-            _jointInverseBindPoseMatrices.reserve(jointCount);
-            unsigned int j = 0;
-            for (std::vector<std::string>::const_iterator i = list.begin(); i != list.end(); ++i)
-            {
-                _jointLookupTable[*i] = j++;
-            }
-            skin->setJointNames(list);
-        }
-    }
-
-    // Make sure we have some joints
-    if (jointCount == 0)
-    {
-        LOG(1, "Warning: No joints found for skin: %s\n", skinElement->getID());
-        return NULL;
-    }
-
-    // Process "INV_BIND_MATRIX" next
-    for (unsigned int i = 0; i < jointInputs.getCount(); ++i)
-    {
-        domInputLocalRef input = jointInputs.get(i);
-        std::string inputSemantic = std::string(input->getSemantic());
-        domURIFragmentType* sourceURI = &input->getSource();
-        sourceURI->resolveElement();
-        domSource* source = (domSource*)(daeElement*)sourceURI->getElement();
-
-        if (equals(inputSemantic, "INV_BIND_MATRIX"))
-        {
-            domListOfFloats& matrixFloats = source->getFloat_array()->getValue();
-            //unsigned int matrixFloatsCount = (unsigned int)source->getFloat_array()->getCount();
-            unsigned int jointIndex = 0;
-
-            for (unsigned int j = 0; j < jointCount; ++j)
-            {
-                Matrix matrix((float)matrixFloats.get(jointIndex + 0), (float)matrixFloats.get(jointIndex + 4), (float)matrixFloats.get(jointIndex + 8), (float)matrixFloats.get(jointIndex + 12),
-                              (float)matrixFloats.get(jointIndex + 1), (float)matrixFloats.get(jointIndex + 5), (float)matrixFloats.get(jointIndex + 9), (float)matrixFloats.get(jointIndex + 13),
-                              (float)matrixFloats.get(jointIndex + 2), (float)matrixFloats.get(jointIndex + 6), (float)matrixFloats.get(jointIndex + 10), (float)matrixFloats.get(jointIndex + 14),
-                              (float)matrixFloats.get(jointIndex + 3), (float)matrixFloats.get(jointIndex + 7), (float)matrixFloats.get(jointIndex + 11), (float)matrixFloats.get(jointIndex + 15));
-
-                _jointInverseBindPoseMatrices.push_back(matrix);
-                jointIndex += 16;
-            }
-        }
-    }
-
-    skin->setBindPoses(_jointInverseBindPoseMatrices);
-
-    // Get the vertex weights inputs
-    domSkin::domVertex_weights* vertexWeights =  skinElement->getVertex_weights();
-    domInputLocalOffset_Array& vertexWeightsInputs = vertexWeights->getInput_array();
-    unsigned int vertexWeightsCount = (unsigned int)vertexWeights->getCount();
-    domListOfFloats jointWeights;
-
-    for (unsigned int i = 0; i < jointInputs.getCount(); ++i)
-    {
-        domInputLocalOffsetRef input = vertexWeightsInputs.get(i);
-        std::string inputSemantic = std::string(input->getSemantic());
-        domURIFragmentType* sourceURI = &input->getSource();
-        sourceURI->resolveElement();
-        domSource* source = (domSource*)(daeElement*)sourceURI->getElement();
-
-        if (equals(inputSemantic, "WEIGHT"))
-        {
-            domFloat_array* weights = source->getFloat_array();
-            if (weights)
-            {
-                jointWeights = weights->getValue();
-            }
-        }
-    }
-    
-    // Get the number of joint influences per vertex
-    domSkin::domVertex_weights::domVcount* vCountElement = vertexWeights->getVcount();
-    domListOfUInts skinVertexInfluenceCounts = vCountElement->getValue();
-    // Get the joint/weight pair data.
-    domSkin::domVertex_weights::domV* vElement = vertexWeights->getV();
-    domListOfInts skinVertexJointWeightPairIndices = vElement->getValue();
-        
-    // Get the vertex influence count for any given vertex (up to max of 4)
-    unsigned int maxVertexInfluencesCount = SCENE_SKIN_VERTEXINFLUENCES_MAX;
-    skin->setVertexInfluenceCount(maxVertexInfluencesCount);
-
-    // Get the vertex blend weights and joint indices and
-    // allocate our vertex blend weights and blend indices arrays.
-    // These will be used and cleaned up later in LoadMesh
-    int skinVertexInfluenceCountTotal = skinVertexInfluenceCounts.getCount();
-    int totalVertexInfluencesCount = vertexWeightsCount * maxVertexInfluencesCount;
-    _vertexBlendWeights = new float[totalVertexInfluencesCount];
-    _vertexBlendIndices = new unsigned int[totalVertexInfluencesCount];
-
-    // Preset the default blend weights to 0.0f (no effect) and blend indices to 0 (uses the first which when multiplied
-    // will have no effect anyhow.
-    memset(_vertexBlendWeights, 0, totalVertexInfluencesCount * sizeof(float));
-    memset(_vertexBlendIndices , 0, totalVertexInfluencesCount * sizeof(unsigned int));
-    
-    int vOffset = 0;
-    int weightOffset = 0;
-
-    // Go through all the skin vertex influence weights from the indexed data.
-    for (int i = 0; i < skinVertexInfluenceCountTotal; ++i)
-    {
-        // Get the influence count and directly get the vertext blend weights and indices.
-        unsigned int vertexInfluenceCount = (unsigned int)skinVertexInfluenceCounts.get(i);
-        float vertexInfluencesTotalWeights = 0.0f;
-        std::vector<SkinnedVertexWeightPair> vertexInfluences;
-        //vertexInfluences.SetCapacity(vertexInfluenceCount);
-
-        // Get the index/weight pairs and some the weight totals while at it.
-        for (unsigned int j = 0; j < vertexInfluenceCount; ++j)
-        {
-            float weight = (float)jointWeights.get((unsigned int)skinVertexJointWeightPairIndices[vOffset + 1]);
-            int index = (int)skinVertexJointWeightPairIndices[vOffset];
-            
-            // Set invalid index corresponding weights to zero
-            if (index < 0 || index > (int)vertexWeightsCount)
-            {
-                weight = 0.0f;
-                index = 0;
-            }
-
-            SkinnedVertexWeightPair pair(weight, index);
-            vertexInfluences.push_back(pair);
-            vertexInfluencesTotalWeights += weight;
-
-            vOffset+=2;
-        }
-
-        // Get up the the maximum vertex weight influence count.
-         for (unsigned int j = 0; j < maxVertexInfluencesCount; ++j)
-        {
-            if (j < vertexInfluenceCount)
-            {
-                SkinnedVertexWeightPair pair = vertexInfluences[j];
-                _vertexBlendIndices[weightOffset] = pair.BlendIndex;
-                    
-                if (vertexInfluencesTotalWeights > 0.0f)
-                {
-                    _vertexBlendWeights[weightOffset] = pair.BlendWeight;
-                }
-                else
-                {
-                    if (j == 0)
-                    {
-                        _vertexBlendWeights[weightOffset] = 1.0f;
-                    }
-                    else
-                    {
-                        _vertexBlendWeights[weightOffset] = 0.0f;
-                    }
-                }
-            }
-
-            weightOffset++;
-        }
-    }
-
-    model->setSkin(skin);
-
-    ///////////////////////////////////////////////////////////
-    // get geometry
-    xsAnyURI geometryURI = skinElement->getSource();
-    domGeometry* geometry = daeSafeCast<domGeometry>(geometryURI.getElement());
-    if (geometry)
-    {
-        const domMesh* meshElement = geometry->getMesh();
-        if (meshElement)
-        {
-            Mesh* mesh = loadMesh(meshElement, geometry->getId());
-            if (mesh)
-            {
-                model->setMesh(mesh);
-            }
-        }
-    }
-    ///////////////////////////////////////////////////////////
-
-    return model;
-}
-
-Model* DAESceneEncoder::loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial)
-{
-    // Does this geometry have a valid mesh?
-    // Get the mesh for the geometry (if it has one)
-    const domMesh* meshElement = geometry->getMesh();
-    if (meshElement == NULL)
-    {
-        LOG(1, "Warning: No mesh found for geometry: %s\n", geometry->getId());
-        return NULL;
-    }
-
-    ///////////////////////////// GEOMETRY
-
-    // Load the mesh for this model
-    Mesh* mesh = loadMesh(meshElement, geometry->getId());
-    if (mesh == NULL)
-    {
-        return NULL;
-    }
-
-    // Mesh instance
-    Model* model = new Model();
-    model->setMesh(mesh);
-    return model;
-}
-
-Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& geometryId)
-{
-    const domTriangles_Array& trianglesArray = meshElement->getTriangles_array();
-    unsigned int trianglesArrayCount = (unsigned int)trianglesArray.getCount();
-
-    // Ensure the data is exported as triangles.
-    if (trianglesArrayCount == 0)
-    {
-        LOG(1, "Warning: Geometry mesh has no triangles: %s\n", geometryId.c_str());
-        return NULL;
-    }
-
-    // Check if this mesh already exists
-    Mesh* mesh = _gamePlayFile.getMesh(geometryId.c_str());
-    if (mesh)
-    {
-        return mesh;
-    }
-    mesh = new Mesh();
-    mesh->setId(geometryId.c_str());
-
-    std::vector<DAEPolygonInput*> polygonInputs;
-
-    // Quickly just go through each triangles array and make sure they have the same number of inputs
-    // with the same layout.
-    // const domSource_Array& sourceArray = meshElement->getSource_array();
-    const domInputLocal_Array& vertexArray = meshElement->getVertices()->getInput_array();
-
-    unsigned int inputCount = (unsigned int)-1;
-
-    // Loop through our set of triangle lists (each list of triangles corresponds to a single MeshPart)
-    for (unsigned int i = 0; i < trianglesArrayCount; ++i)
-    {
-        const domTrianglesRef& triangles = trianglesArray.get(i);
-        const domInputLocalOffset_Array& inputArray = triangles->getInput_array();
-
-        // If not set then determine the number of input for all the triangles.
-        if (inputCount == -1)
-        {
-            inputCount = (unsigned int)inputArray.getCount();
-
-            int texCoordCount = 0;
-            for (unsigned int j = 0; j < inputCount; ++j)
-            {
-                const domInputLocalOffsetRef& input = inputArray.get(j);
-                std::string inputSemantic = input->getSemantic();
-
-                // If its a vertex first do an extra lookup for the inclusive inputs
-                if (equals(inputSemantic, "VERTEX"))
-                {
-                    unsigned int vertexArrayCount = (unsigned int)vertexArray.getCount();
-                    for (unsigned int k = 0; k < vertexArrayCount; ++k)
-                    {
-                        const domInputLocalRef& vertexInput = vertexArray.get(k);
-                        
-                        std::string semantic = std::string(vertexInput->getSemantic());
-                        int type = getVertexUsageType(semantic);
-                        if (type == -1)
-                        {
-                            LOG(1, "Warning: Vertex semantic (%s) is invalid/unsupported for geometry mesh: %s\n", semantic.c_str(), geometryId.c_str());
-                        }
-
-                        DAEPolygonInput* polygonInput = new DAEPolygonInput();
-                        domURIFragmentType& sourceURI = vertexInput->getSource();
-                        sourceURI.resolveElement();
-                        domSource* source = (domSource*)(daeElement*)sourceURI.getElement();
-                        polygonInput->offset = 0;
-                        polygonInput->sourceValues = source->getFloat_array()->getValue();
-                        polygonInput->type = type;
-                        polygonInputs.push_back(polygonInput);
-                    }
-                }
-                else
-                {
-                    std::string semantic = input->getSemantic();
-                    int type = getVertexUsageType(semantic);
-                    if (type == -1)
-                    {
-                        LOG(1, "Warning: Semantic (%s) is invalid/unsupported for geometry mesh: %s\n", semantic.c_str(), geometryId.c_str());
-                        break;
-                    }
-                    if (type == TEXCOORD0)
-                    {
-                        // Some meshes have multiple texture coordinates
-                        assert(texCoordCount <= 7);
-                        type += texCoordCount;
-                        ++texCoordCount;
-                    }
-
-                    DAEPolygonInput* polygonInput = new DAEPolygonInput();
-                    domURIFragmentType& sourceURI = input->getSource();
-                    sourceURI.resolveElement();
-                    domSource* source = (domSource*)(daeElement*)sourceURI.getElement();
-                    polygonInput->offset = (unsigned int)input->getOffset();
-                    polygonInput->sourceValues = source->getFloat_array()->getValue();
-                    polygonInput->type = type;
-
-                    // Get the accessor info
-                    const domSource::domTechnique_commonRef& technique = source->getTechnique_common();
-                    if (technique.cast())
-                    {
-                        const domAccessorRef& accessor = technique->getAccessor();
-                        polygonInput->accessor = accessor;
-                    }
-
-                    polygonInputs.push_back(polygonInput);
-                }
-            }
-        }
-        else
-        {
-            // If there is a triangle array with a different number of inputs, this is not supported.
-            if (inputCount != (unsigned int)inputArray.getCount())
-            {
-                for (size_t j = 0; j < polygonInputs.size(); ++j)
-                {
-                    delete polygonInputs[j];
-                }
-                LOG(1, "Warning: Triangles do not all have the same number of input sources for geometry mesh: %s\n", geometryId.c_str());
-                return NULL;
-            }
-            else
-            {
-                // TODO: Check if they are in the same order...
-            }
-        }
-    }
-    
-    // Now we have validated that all input in all triangles are the same and in the same input layout.
-    // Lets start to read them and build our subsets.
-    for (unsigned int i = 0; i < trianglesArrayCount; ++i)
-    {
-        // Subset to be built.
-        MeshPart* subset = new MeshPart();
-
-        // All of the information about the triangles and the sources to access the data from.
-        domTriangles* triangles = daeSafeCast<domTriangles>(trianglesArray.get(i));
-
-        // Parse the material for this subset
-        //string materialName = triangles->getMaterial() == NULL ? _T("") : triangles->getMaterial();
-        //if (materialName.size() > 0)
-        ///    subset->material = ParseMaterial(bindMaterial, materialName);
-
-        //const domInputLocalOffset_Array& inputArray = triangles->getInput_array();
-        const domListOfUInts& polyInts = triangles->getP()->getValue();
-        unsigned int polyIntsCount = (unsigned int)polyInts.getCount();
-        unsigned int poly = 0;
-        unsigned int inputSourceCount = (unsigned int)polygonInputs.size();
-        unsigned int maxOffset = 0;
-
-        // Go through the polygon indices for each input source retrieve the values
-        // and iterate by its offset.
-
-        Vertex vertex;
-        for (unsigned int k = 0; k < inputSourceCount && poly < polyIntsCount;)
-        {
-            const domListOfFloats& source = polygonInputs[k]->sourceValues;
-            unsigned int offset = polygonInputs[k]->offset;
-            if (offset > maxOffset)
-            {
-                maxOffset = offset;
-            }
-            int polyIndexInt = (int) polyInts.get(poly + offset);
-            unsigned int polyIndex = (unsigned int) polyInts.get(poly + offset);
-
-            switch (polygonInputs[k]->type)
-            {
-            case POSITION:
-                vertex = Vertex(); // TODO
-                if (_vertexBlendWeights && _vertexBlendIndices)
-                {
-                    vertex.hasWeights = true;
-
-                    vertex.blendWeights.x =  _vertexBlendWeights[polyIndex * 4];
-                    vertex.blendWeights.y =  _vertexBlendWeights[polyIndex * 4 + 1];
-                    vertex.blendWeights.z =  _vertexBlendWeights[polyIndex * 4 + 2];
-                    vertex.blendWeights.w =  _vertexBlendWeights[polyIndex * 4 + 3];
-
-                    vertex.blendIndices.x =  (float)_vertexBlendIndices[polyIndex * 4];
-                    vertex.blendIndices.y =  (float)_vertexBlendIndices[polyIndex * 4 + 1];
-                    vertex.blendIndices.z =  (float)_vertexBlendIndices[polyIndex * 4 + 2];
-                    vertex.blendIndices.w =  (float)_vertexBlendIndices[polyIndex * 4 + 3];
-                }
-
-                vertex.position.x = (float)source.get(polyIndex * 3);
-                vertex.position.y = (float)source.get(polyIndex * 3 + 1);
-                vertex.position.z = (float)source.get(polyIndex * 3 + 2);
-                break;
-        
-            case NORMAL:
-                vertex.hasNormal = true;
-                vertex.normal.x = (float)source.get(polyIndex * 3);
-                vertex.normal.y = (float)source.get(polyIndex * 3 + 1);
-                vertex.normal.z = (float)source.get(polyIndex * 3 + 2);
-                break;
-
-            // TODO: We must examine the Collada input accessor and read the stride/count to verify this - not ONLY for Color, but we should be doing this for ALL components (i.e. Position, Normal, etc).
-            case COLOR:
-            {
-                domAccessor* accessor = polygonInputs[k]->accessor;
-                if (accessor)
-                {
-                    vertex.hasDiffuse = true;
-                    vertex.diffuse.w = 1.0f;
-                    unsigned int stride = (unsigned int)polygonInputs[k]->accessor->getStride();
-                    unsigned int index = polyIndex * stride;
-
-                    const domParam_Array& paramArray = accessor->getParam_array();
-                    const size_t paramArrayCount = paramArray.getCount();
-
-                    for (size_t i = 0; i < paramArrayCount; ++i)
-                    {
-                        const domParamRef& param = paramArray.get(i);
-                        const char* name = param->getName();
-                        if (name)
-                        {
-                            switch (name[0])
-                            {
-                            case 'r':
-                            case 'R':
-                                vertex.diffuse.x = (float)source.get(index + i); // red
-                                break;
-                            case 'g':
-                            case 'G':
-                                vertex.diffuse.y = (float)source.get(index + i); // green
-                                break;
-                            case 'b':
-                            case 'B':
-                                vertex.diffuse.z = (float)source.get(index+ i ); // blue
-                                break;
-                            case 'a':
-                            case 'A':
-                                vertex.diffuse.w = (float)source.get(index + i); // alpha
-                                break;
-                            default:
-                                break;
-                            }
-                        }
-                    }
-                }
-                break;
-            }
-
-            case TANGENT:
-                vertex.hasTangent = true;
-                vertex.tangent.x = (float)source.get(polyIndex * 3);
-                vertex.tangent.y = (float)source.get(polyIndex * 3 + 1);
-                vertex.tangent.z = (float)source.get(polyIndex * 3 + 2);
-                break;
-
-            case BINORMAL:
-                vertex.hasBinormal = true;
-                vertex.binormal.x = (float)source.get(polyIndex * 3);
-                vertex.binormal.y = (float)source.get(polyIndex * 3 + 1);
-                vertex.binormal.z = (float)source.get(polyIndex * 3 + 2);
-                break;
-
-            case TEXCOORD0:
-            case TEXCOORD1:
-            case TEXCOORD2:
-            case TEXCOORD3:
-            case TEXCOORD4:
-            case TEXCOORD5:
-            case TEXCOORD6:
-            case TEXCOORD7:
-                {
-                    unsigned int index = polygonInputs[k]->type - TEXCOORD0;
-                    //for (unsigned int i = 0; i < uvSetCount; ++i)
-                    //{
-                        vertex.hasTexCoord[index] = true;
-                        if (polygonInputs[k]->accessor)
-                        {
-                            // TODO: This assumes (s, t) are first
-                            unsigned int stride = (unsigned int)polygonInputs[k]->accessor->getStride();
-                            if (polyIndexInt < 0)
-                            {
-                                unsigned int i = (unsigned int)((int)polygonInputs[k]->accessor->getCount()) + polyIndexInt;
-                                vertex.texCoord[index].x = (float)source.get(i * stride);
-                                vertex.texCoord[index].y = (float)source.get(i * stride + 1);
-                            }
-                            else
-                            {
-                                vertex.texCoord[index].x = (float)source.get(polyIndex * stride);
-                                vertex.texCoord[index].y = (float)source.get(polyIndex * stride + 1);
-                            }
-                        }
-                        else
-                        {
-                            vertex.texCoord[index].x = (float)source.get(polyIndex * 2);
-                            vertex.texCoord[index].y = (float)source.get(polyIndex * 2 + 1);
-                        }
-                    //}
-                }
-                break;
-
-            default:
-                break;
-            }
-
-            // On the last input source attempt to add the vertex or index an existing one.
-            if (k == (inputSourceCount - 1))
-            {
-                // Only add unique vertices, use a hashtable and compare the hash functions of the
-                // vertices. If they exist simply lookup the index of the existing ones.
-                // otherwise add and new one and index it.
-                unsigned int index;
-                if (mesh->contains(vertex))
-                {
-                    index = mesh->getVertexIndex(vertex);
-                }
-                else
-                {
-                    index = mesh->addVertex(vertex);
-                }
-                subset->addIndex(index);
-
-                poly += (maxOffset+1);
-                k = 0;
-            }
-            else
-            {
-                k++;
-            }
-        }
-        // Add our new subset for the mesh.
-        mesh->addMeshPart(subset);
-    }
-
-    // The order that the vertex elements are add to the list matters.
-    // It should be the same order as how the Vertex data is written.
-
-    // Position
-    mesh->addVetexAttribute(POSITION, Vertex::POSITION_COUNT);
-    
-    // Normals
-    if (mesh->vertices[0].hasNormal)
-    {
-        mesh->addVetexAttribute(NORMAL, Vertex::NORMAL_COUNT);
-    }
-    // Tangents
-    if (mesh->vertices[0].hasTangent)
-    {
-        mesh->addVetexAttribute(TANGENT, Vertex::TANGENT_COUNT);
-    }
-    // Binormals
-    if (mesh->vertices[0].hasBinormal)
-    {
-        mesh->addVetexAttribute(BINORMAL, Vertex::BINORMAL_COUNT);
-    }
-    // Texture Coordinates
-    for (unsigned int i = 0; i < MAX_UV_SETS; ++i)
-    {
-        if (mesh->vertices[0].hasTexCoord[i])
-        {
-            mesh->addVetexAttribute(TEXCOORD0 + i, Vertex::TEXCOORD_COUNT);
-        }
-    }
-    // Diffuse Color
-    if (mesh->vertices[0].hasDiffuse)
-    {
-        mesh->addVetexAttribute(COLOR, Vertex::DIFFUSE_COUNT);
-    }
-    // Skinning BlendWeights BlendIndices
-    if (mesh->vertices[0].hasWeights)
-    {
-        mesh->addVetexAttribute(BLENDWEIGHTS, Vertex::BLEND_WEIGHTS_COUNT);
-        mesh->addVetexAttribute(BLENDINDICES, Vertex::BLEND_INDICES_COUNT);
-    }
-
-    _gamePlayFile.addMesh(mesh);
-    return mesh;
-}
-
-int DAESceneEncoder::getVertexUsageType(const std::string& semantic)
-{
-    if (semantic.length() > 0)
-    {
-        switch (semantic[0])
-        {
-        case 'P':
-            if (equals(semantic, "POSITION"))
-            {
-                return POSITION;
-            }
-        case 'N':
-            if (equals(semantic, "NORMAL"))
-            {
-                return NORMAL;
-            }
-        case 'C':
-            if (equals(semantic, "COLOR"))
-            {
-                return COLOR;
-            }
-        case 'T':
-            if (equals(semantic, "TANGENT"))
-            {
-                return TANGENT;
-            }
-            else if (equals(semantic, "TEXCOORD"))
-            {
-                return TEXCOORD0;
-            }
-            else if (equals(semantic, "TEXTANGENT"))
-            {
-                // Treat TEXTANGENT as TANGENT
-                return TANGENT;
-            }
-            else if (equals(semantic, "TEXBINORMAL"))
-            {
-                // Treat TEXBINORMAL as BINORMAL
-                return BINORMAL;
-            }
-        case 'B':
-            if (equals(semantic, "BINORMAL"))
-            {
-                return BINORMAL;
-            }
-        default:
-            return -1;
-        }
-    }
-    return -1;
-}
-
-DAESceneEncoder::DAEPolygonInput::DAEPolygonInput(void) :
-    offset(0),
-    type(0),
-    accessor(NULL)
-{
-}
-
-DAESceneEncoder::DAEPolygonInput::~DAEPolygonInput(void)
-{
-}
-
-}

+ 0 - 211
gameplay-encoder/src/DAESceneEncoder.h

@@ -1,211 +0,0 @@
-#ifndef DAESCENEEENCODER_H_
-#define DAESCENEEENCODER_H_
-
-#include "StringUtil.h"
-#include "Object.h"
-#include "Node.h"
-#include "Camera.h"
-#include "Light.h"
-#include "Mesh.h"
-#include "MeshPart.h"
-#include "MeshSkin.h"
-#include "Model.h"
-#include "Scene.h"
-#include "Animation.h"
-#include "AnimationChannel.h"
-#include "Vertex.h"
-#include "Matrix.h"
-#include "Transform.h"
-#include "DAEChannelTarget.h"
-#include "GPBFile.h"
-#include "DAEUtil.h"
-#include "EncoderArguments.h"
-
-namespace gameplay
-{
-
-/**
- * Class for binary encoding a Collada (DAE) file.
- */
-class DAESceneEncoder
-{
-public:
-
-    static const unsigned int SCENE_SKIN_VERTEXINFLUENCES_MAX = 4;
-    
-    /**
-     * Constructor.
-     */
-    DAESceneEncoder();
-    
-    /**
-     * Destructor.
-     */
-    ~DAESceneEncoder();
-    
-    /**
-     * Writes out encoded Collada 1.4 file.
-     */
-    void write(const std::string& filepath, const EncoderArguments& arguments);
-
-private:
-
-    class DAEPolygonInput
-    {
-    public:
-        DAEPolygonInput(void);
-        /**
-         * Destructor.
-         */
-        virtual ~DAEPolygonInput(void);
-
-        unsigned int offset;
-        int type;
-        domListOfFloats sourceValues;
-        domAccessor* accessor;
-    };
-
-    class SkinnedVertexWeightPair
-    {
-    public:
-        float BlendWeight;
-        unsigned int BlendIndex;
-
-        SkinnedVertexWeightPair(float blendWeight, unsigned int blendIndex) : BlendWeight(blendWeight), BlendIndex(blendIndex)
-        {
-        }
-
-        bool operator < (const SkinnedVertexWeightPair& value) const
-        {
-            return value.BlendWeight < BlendWeight;
-        }
-    };
-
-    /**
-     * Optimizes the COLLADA dom based on the arguments passed to the encoder.
-     * 
-     * @param arguments The command line arguments passed to the encoder.
-     * @param dom The COLLADA dom.
-     */
-    void optimizeCOLLADA(const EncoderArguments& arguments, domCOLLADA* dom);
-    
-    void triangulate(DAE* dae);
-    
-    void createTrianglesFromPolygons(domMesh* domMesh, domPolygons* domPolygons);
-    void createTrianglesFromPolylist(domMesh* domMesh, domPolylist* domPolylist);
-
-    Node* loadNode(domNode* node, Node* parent);
-
-    void loadScene(const domVisual_scene* visualScene);
-    void loadCameraInstance(const domNode* n, Node* node);
-    void loadControllerInstance(const domNode* n, Node* node);
-    void loadLightInstance(const domNode* n, Node* node);
-    void loadGeometryInstance(const domNode* n, Node* node);
-
-    /**
-     * Loads all animations from the given COLLADA dom into the GamePlayFile.
-     */
-    void loadAnimations(const domCOLLADA* dom);
-
-    /**
-     * Loads a COLLADA animation element.
-     * 
-     * @param animationRef The animation dom element to load from.
-     * @param altId   The id string to use if the animation doesn't have an id.
-     */
-    void loadAnimation(const domAnimationRef animationRef, const char* altId = NULL);
-
-    Camera* loadCamera(const domCamera* cameraRef);
-    Light* loadLight(const domLight* lightRef);
-    Model* loadSkin(const domSkin* skinElement);
-    Model* loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial);
-
-    void loadSkeleton(domNode* rootNode, MeshSkin* skin);
-    void loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin);
-    
-    /**
-     * Loads interpolation curve data from the given source into the animation.
-     * 
-     * @param source The source dom element to load interpolation curves from.
-     * @param animation The destination animation to copy to.
-     */
-    void loadInterpolation(const domSourceRef source, AnimationChannel* animation);
-
-    /**
-     * Returns the active camera node for the given scene.
-     * 
-     * @param visualScene The collada visual scene node.
-     * @param scene The gameplay scene node.
-     * 
-     * @return The active camera node or NULL if one was not found.
-     */
-    Node* findSceneActiveCameraNode(const domVisual_scene* visualScene, Scene* scene);
-
-    /**
-     * Loads and returns a mesh (geometry). If the mesh has already been loaded, it is simply returned.
-     */
-    Mesh* loadMesh(const domMesh* meshElement, const std::string& geometryId);
-
-    /**
-     * Sets the transform of node from the domNode transform.
-     */
-    void transformNode(domNode* domNode, Node* node);
-
-    /**
-     * Calculates the transform from a domNode. (<translate>, <rotate>, <scale>)
-     */
-    void calcTransform(domNode* domNode, Matrix& dstTransform);
-
-    /**
-     * Loads the target data into the animation from the given channel's target.
-     * Example: <channel target="Cube/location.X" />
-     * Target ID is "Cube"
-     * The target attribute is "location.X"
-     * 
-     * @param channelRef The channel dom element.
-     * @param animationChannel The animation to copy to.
-     * 
-     * @return True if the animation channel data was loaded without error; false if there was an error.
-     */
-    bool loadTarget(const domChannelRef& channelRef, AnimationChannel* animationChannel);
-
-    void begin();
-    void end(const char* str);
-
-    /**
-     * Copies float values from a domFloat_array to a std::vector<float>.
-     * 
-     * @param source Source of float values to copy from.
-     * @param target Destination float vector to copy to.
-     */
-    static void copyFloats(const domFloat_array* source, std::vector<float>* target);
-
-    /**
-     * Returns the VertexUsage value for the given semantic string.
-     * 
-     * @param semantic The semantic attribute string from the COLLADA <input> element.
-     * 
-     * @return The VertexUsage or -1 if the string was not recognized.
-     */
-    static int getVertexUsageType(const std::string& semantic);
-    
-private:
-    
-    DAE* _collada;        // Collada datastore in memory to read from.
-    domCOLLADA* _dom;
-    FILE* file;        // Output file to write to.
-    GPBFile _gamePlayFile;
-
-    std::map<std::string, int> _jointLookupTable;
-    std::vector<Matrix>_jointInverseBindPoseMatrices;
-    float* _vertexBlendWeights;
-    unsigned int* _vertexBlendIndices;
-
-    std::vector<std::string> _tempGroupAnimationIds;
-
-    clock_t _begin;
-};
-
-}
-
-#endif

+ 0 - 554
gameplay-encoder/src/DAEUtil.cpp

@@ -1,554 +0,0 @@
-#include <set>
-#include "Base.h"
-#include "DAEUtil.h"
-#include "StringUtil.h"
-
-namespace gameplay
-{
-
-/**
- * Returns the index of the skeleton in skeletonArray that points to the given node.
- * 
- * @param skeletonArray The array of skeletons to search.
- * @param node The target node.
- * 
- * @return The index in skeletonArray or -1 if not found.
- */
-static int getIndex(const domInstance_controller::domSkeleton_Array& skeletonArray, const domNodeRef& node);
-
-/**
- * Gets all of the animation channels that target the given node and appends them to the list.
- * 
- * @param animationRef The animation to search in.
- * @param nodeIdSlash The node's id with a forward slash appended to it.
- * @param channels The list of channels to append to.
- */
-static void getAnimationChannels(const domAnimationRef& animationRef, const std::string& nodeIdSlash, std::list<domChannelRef>& channels);
-
-void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels)
-{
-    assert(node->getId());
-    std::string nodeIdSlash (node->getId());
-    nodeIdSlash.append("/");
-
-    domCOLLADA* root = (domCOLLADA*)node->getDocument()->getDomRoot();
-
-    domLibrary_animations_Array& animationLibrary = root->getLibrary_animations_array();
-    size_t animationLibraryCount = animationLibrary.getCount();
-    for (size_t i = 0; i < animationLibraryCount; ++i)
-    {
-        domLibrary_animationsRef& animationsRef = animationLibrary.get(i);
-        domAnimation_Array& animationArray = animationsRef->getAnimation_array();
-        size_t animationCount = animationArray.getCount();
-        for (size_t j = 0; j < animationCount; ++j)
-        {
-            domAnimationRef& animationRef = animationArray.get(j);
-            getAnimationChannels(animationRef, nodeIdSlash, channels);
-        }
-    }
-
-    // Recursively do the same for all nodes
-    daeTArray< daeSmartRef<daeElement> > children;
-    node->getChildren(children);
-    size_t childCount = children.getCount();
-    for (size_t i = 0; i < childCount; ++i)
-    {
-        daeElementRef childElement = children[i];
-        if (childElement->typeID() == domNode::ID())
-        {
-            domNodeRef childNode = daeSafeCast<domNode>(childElement);
-            getAnimationChannels(childNode, channels);
-        }
-    }
-}
-
-void getJointNames(const domSourceRef source, std::vector<std::string>& list)
-{
-    // BLENDER used name_array
-    const domName_arrayRef& nameArray = source->getName_array();
-    if (nameArray.cast())
-    {
-        domListOfNames& ids = nameArray->getValue();
-        size_t jointCount = (size_t)nameArray->getCount();
-        for (size_t j = 0; j < jointCount; ++j)
-        {
-            list.push_back(std::string(ids.get(j)));
-        }
-    }
-    else
-    {
-        // Seymour used IDREF_array
-        const domIDREF_arrayRef& idArray = source->getIDREF_array();
-        if (idArray.cast())
-        {
-            xsIDREFS& ids = idArray->getValue();
-            size_t jointCount = (size_t)idArray->getCount();
-            for (size_t j = 0; j < jointCount; ++j)
-            {
-                list.push_back(std::string(ids.get(j).getID()));
-            }
-        }
-    }
-}
-
-void getJointNames(const domSkin* skin, std::vector<std::string>& list)
-{
-    const domSkin::domJointsRef& joints = skin->getJoints();
-    const domInputLocal_Array& inputArray = joints->getInput_array();
-    size_t inputCount = inputArray.getCount();
-    for (size_t i = 0; i < inputCount; ++i)
-    {
-        const domInputLocalRef input = inputArray.get(i);
-        const char* semantic = input->getSemantic();
-        if (strcmp(semantic, "JOINT") == 0)
-        {
-            daeElement* sourceElement = input->getSource().getElement();
-            if (sourceElement)
-            {
-                const domSourceRef source = daeSafeCast<domSource>(sourceElement);
-                getJointNames(source, list);
-            }
-        }
-    }
-}
-
-domSource* getInputSource(const domChannelRef& channel)
-{
-    daeElement* element = channel->getSource().getElement();
-    if (element && element->typeID() == domSampler::ID())
-    {
-        domSampler* sampler = daeSafeCast<domSampler>(element);
-        const domInputLocal_Array& inputArray = sampler->getInput_array();
-        size_t inputArrayCount = inputArray.getCount();
-        for (size_t i = 0; i < inputArrayCount; ++i)
-        {
-            const domInputLocalRef& input = inputArray.get(i);
-            if (strcmp(input->getSemantic(), "INPUT") == 0)
-            {
-                daeElement* e = input->getSource().getElement();
-                if (e && e->typeID() == domSource::ID())
-                {
-                    domSource* source = daeSafeCast<domSource>(e);
-                    assert(source);
-                    return source;
-                }
-            }
-        }
-    }
-    return NULL;
-}
-
-const domSamplerRef getSampler(const domChannelRef& channel)
-{
-    const domURIFragmentType& uri = channel->getSource();
-    daeElementRef element = uri.getElement();
-    if (element && element->typeID() == domSampler::ID())
-    {
-        const domSamplerRef sampler = daeSafeCast<domSampler>(element);
-        return sampler;
-    }
-    // resolve the source manually by searching for the sampler in the animation that the channel is a child of.
-    const std::string& id = uri.id();
-    const daeElementRef& parent = channel->getParent();
-    if (parent && parent->typeID() == domAnimation::ID())
-    {
-        const domAnimationRef animation = daeSafeCast<domAnimation>(parent);
-        
-        const domSampler_Array& samplerArray = animation->getSampler_array();
-        size_t count = samplerArray.getCount();
-        for (size_t i = 0; i < count; ++i)
-        {
-            const domSamplerRef& sampler = samplerArray.get(i);
-            if (id.compare(sampler->getId()) == 0)
-            {
-                return sampler;
-            }
-        }
-    }
-    return NULL;
-}
-
-const domSourceRef getSource(const domInputLocalRef& inputLocal, const domAnimationRef& animation)
-{
-    const domURIFragmentType& uri = inputLocal->getSource();
-    daeElementRef element = uri.getElement();
-    if (element && element->typeID() == domSampler::ID())
-    {
-        const domSourceRef source = daeSafeCast<domSource>(element);
-        return source;
-    }
-    // Resolve the URI by searching through the animation's list of sources
-    const std::string& id = uri.id();
-    const domSource_Array& sourceArray = animation->getSource_array();
-    size_t count = sourceArray.getCount();
-    for (size_t i = 0; i < count; ++i)
-    {
-        const domSourceRef source = sourceArray.get(i);
-        if (id.compare(source->getId()) == 0)
-        {
-            return source;
-        }
-    }
-    return NULL;
-}
-
-const domName_arrayRef getSourceNameArray(const domSourceRef& source)
-{
-    const domName_arrayRef& nameArray = source->getName_array();
-    if (nameArray)
-    {
-        return nameArray;
-    }
-    daeTArray<daeSmartRef<daeElement> > children;
-    source->getChildren(children);
-    size_t childCount = children.getCount();
-    for (size_t i = 0; i < childCount; ++i)
-    {
-        const daeElementRef element = children.get(i);
-        if (element->typeID() == domName_array::ID())
-        {
-            return daeSafeCast<domName_array>(element);
-        }
-    }
-    return NULL;
-}
-
-const domInstance_controller::domSkeletonRef getSkeleton(const domInstance_controllerRef& instanceController)
-{
-    domInstance_controller::domSkeleton_Array& skeletonArray = instanceController->getSkeleton_array();
-    size_t count = skeletonArray.getCount();
-    if (count == 0)
-    {
-        return NULL;
-    }
-    if (count == 1)
-    {
-        return skeletonArray.get(0);
-    }
-    // Maya sometimes outputs multiple skeleton elements.
-    // Find the skeleton element that points to the root most node.
-    const domInstance_controller::domSkeletonRef& currentSkeleton = skeletonArray.get(0);
-    const daeElementRef element = currentSkeleton->getValue().getElement();
-    if (element && element->typeID() == domNode::ID())
-    {
-        domNode* node = daeSafeCast<domNode>(element);
-        int index = 0;
-        bool loop = true;
-        do
-        {
-            daeElementRef parent = node->getParent();
-            if (parent && parent->typeID() == domNode::ID())
-            {
-                domNodeRef parentNode = daeSafeCast<domNode>(parent);
-                int result = getIndex(skeletonArray, parentNode);
-                if (result >= 0)
-                {
-                    index = result;
-                }
-                node = parentNode;
-            }
-            else
-            {
-                loop = false;
-            }
-        } while (loop);
-        if (index >= 0)
-        {
-            return skeletonArray.get(index);
-        }
-    }
-    return NULL;
-}
-
-domNode* getRootJointNode(const domSkin* skin)
-{
-    std::vector<std::string> names;
-    getJointNames(skin, names);
-    daeSIDResolver resolver(const_cast<domSkin*>(skin)->getDocument()->getDomRoot(), names[0].c_str());
-    daeElement* element = resolver.getElement();
-    if (element && element->typeID() == domNode::ID())
-    {
-        domNode* node = daeSafeCast<domNode>(resolver.getElement());
-        return node;
-    }
-    return NULL;
-}
-
-bool equalKeyTimes(const domSource* s1, const domSource* s2)
-{
-    // TODO: shouldn't assume that the source has a float array.
-    const domFloat_arrayRef& f1 = s1->getFloat_array();
-    const domFloat_arrayRef& f2 = s2->getFloat_array();
-    if (f1->getCount() == f2->getCount())
-    {
-        const domListOfFloats& list1 = f1->getValue();
-        const domListOfFloats& list2 = f2->getValue();
-
-        size_t count = (size_t)f1->getCount();
-        for (size_t i = 0; i < count; ++i)
-        {
-            if (list1.get(i) != list2.get(i))
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-    return false;
-}
-
-bool equalKeyTimes(const domChannelRef& c1, const domChannelRef& c2)
-{
-    domSource* s1 = getInputSource(c1);
-    domSource* s2 = getInputSource(c2);
-    assert(s1);
-    assert(s2);
-    return equalKeyTimes(s1, s2);
-}
-
-void moveChannelAndSouresToAnimation(domChannelRef& channel, domAnimationRef& animation)
-{
-    assert(channel);
-    assert(animation);
-
-    daeElement::removeFromParent(channel);
-    animation->add(channel); // move channel
-
-    daeElementRef element = channel->getSource().getElement();
-    if (element)
-    {
-        domSamplerRef sampler = daeSafeCast<domSampler>(element);
-
-        domInputLocal_Array& inputArray = sampler->getInput_array();
-        size_t inputArrayCount = inputArray.getCount();
-        for (size_t i = 0; i < inputArrayCount; ++i)
-        {
-            inputArray = sampler->getInput_array();
-            const domInputLocalRef& input = inputArray.get(i);
-            daeElementRef element = input->getSource().getElement();
-            if (element && element->typeID() == domSource::ID())
-            {
-                domSourceRef source = daeSafeCast<domSource>(element);
-                assert(source);
-                daeElement::removeFromParent(source);
-                animation->add(source); // move source
-            }
-        }
-        daeElement::removeFromParent(sampler);
-        animation->add(sampler); // move sampler
-    }
-}
-
-bool isEmptyAnimation(domAnimationRef& animation)
-{
-    return animation->getAnimation_array().getCount() == 0 &&
-           animation->getChannel_array().getCount() == 0 &&
-           animation->getSampler_array().getCount() == 0 &&
-           animation->getSource_array().getCount() == 0;
-}
-
-int getIndex(const domInstance_controller::domSkeleton_Array& skeletonArray, const domNodeRef& node)
-{
-    const std::string nodeId = node->getId();
-    size_t count = skeletonArray.getCount();
-    for (size_t i = 0; i < count; ++i)
-    {
-        const domInstance_controller::domSkeletonRef& skeleton = skeletonArray.get(i);
-        daeElementRef element = skeleton->getValue().getElement();
-        if (element->typeID() == domNode::ID())
-        {
-            domNodeRef targetNode = daeSafeCast<domNode>(element);
-            if (nodeId.compare(targetNode->getId()) == 0)
-            {
-                return i;
-            }
-        }
-    }
-    return -1;
-}
-
-void getAnimationChannels(const domAnimationRef& animationRef, const std::string& nodeIdSlash, std::list<domChannelRef>& channels)
-{
-    domChannel_Array& channelArray = animationRef->getChannel_array();
-    size_t channelArrayCount = channelArray.getCount();
-    for (size_t k = 0; k < channelArrayCount; ++k)
-    {
-        domChannelRef& channel = channelArray.get(k);
-        const char* target = channel->getTarget();
-
-        // TODO: Assumes only one target per channel?
-        if (startsWith(target, nodeIdSlash.c_str()))
-        {
-            channels.push_back(channel);
-        }
-    }
-
-    // This animation could have child animations.
-    const domAnimation_Array& animationArray = animationRef->getAnimation_array();
-    unsigned int animationCount = animationArray.getCount();
-    for (size_t i = 0; i < animationCount; ++i)
-    {
-        getAnimationChannels(animationArray[i], nodeIdSlash, channels);
-    }
-}
-
-domVisual_scene* getVisualScene(const domCOLLADA::domSceneRef& domScene)
-{
-    daeElement* scene = domScene->getInstance_visual_scene()->getUrl().getElement();
-    if (scene->typeID() == domVisual_scene::ID())
-    {
-        return static_cast<domVisual_scene*>(scene);
-    }
-
-    // DAE_FBX sometimes doesn't export an ID. In that case, see if there is only one visual scene and use that.
-    // Most of the time there is only one visual scene.
-    domCOLLADA* root = (domCOLLADA*)domScene->getDocument()->getDomRoot();
-    domLibrary_visual_scenes_Array& visualSceneLibrary = root->getLibrary_visual_scenes_array();
-    size_t visualSceneLibraryCount = visualSceneLibrary.getCount();
-    for (size_t i = 0; i < visualSceneLibraryCount; ++i)
-    {
-        domLibrary_visual_scenesRef scenesRef = visualSceneLibrary.get(i);
-        domVisual_scene_Array visualScenes = scenesRef->getVisual_scene_array();
-        size_t visualSceneCount = visualScenes.getCount();
-        for (size_t j = 0; j < visualSceneCount; ++j)
-        {
-            domVisual_sceneRef visualScene = visualScenes.get(j);
-            if (domVisual_scene* v = visualScene.cast())
-            {
-                return v;
-            }
-        }
-    }
-    return NULL;
-}
-
-domNode* getParent(domNodeRef node)
-{
-    daeElement* parent = node->getParent();
-    if (parent && parent->typeID() == domNode::ID())
-    {
-        domNodeRef parentNode = daeSafeCast<domNode>(parent);
-        return parentNode.cast();
-    }
-    return NULL;
-}
-
-domAnimation* getAnimation(domChannelRef channel)
-{
-    daeElement* parent = channel->getParent();
-    if (parent && parent->typeID() == domAnimation::ID())
-    {
-        domAnimationRef parentNode = daeSafeCast<domAnimation>(parent);
-        return parentNode.cast();
-    }
-    return NULL;
-}
-
-domNode* getCommonNodeAncestor(std::list<domNodeRef>& nodes)
-{
-    if (nodes.empty())
-        return NULL;
-    if (nodes.size() == 1)
-        return nodes.begin()->cast();
-
-    std::list<domNode*> ancestors;
-    size_t minAncestorCount = INT_MAX;
-    for (std::list<domNodeRef>::iterator it = nodes.begin(); it != nodes.end(); ++it)
-    {
-        domNodeRef& node = *it;
-        getNodeAncestors(node, ancestors);
-        ancestors.push_back(node.cast());
-        minAncestorCount = std::min(minAncestorCount, ancestors.size());
-    }
-    ancestors.resize(minAncestorCount);
-
-    return ancestors.back();
-}
-
-void getNodeAncestors(domNodeRef& node, std::list<domNode*>& ancestors)
-{
-    ancestors.clear();
-    domNode* parent = getParent(node);
-    while (parent != NULL)
-    {
-        ancestors.push_front(parent);
-        parent = getParent(parent);
-    }
-}
-
-bool findGroupAnimationNodes(domCOLLADA* dom, std::vector<std::string>& nodesToGroup)
-{
-    bool groupPossible = false;
-    const domLibrary_controllers_Array& controllersArrays = dom->getLibrary_controllers_array();
-    size_t controllersArraysCount = controllersArrays.getCount();
-    for (size_t i = 0; i < controllersArraysCount; ++i)
-    {
-        const domLibrary_controllersRef& libraryController = controllersArrays.get(i);
-        const domController_Array& controllerArray = libraryController->getController_array();
-        size_t controllerCount = controllerArray.getCount();
-        for (size_t j = 0; j < controllerCount; ++j)
-        {
-            const domControllerRef& controllerRef = controllerArray.get(j);
-            const domSkinRef& skinRef = controllerRef->getSkin();
-            if (skinRef.cast() != NULL)
-            {
-                domSkin::domJointsRef joints = skinRef->getJoints();
-                domInputLocal_Array& jointInputs = joints->getInput_array();
-                for (unsigned int i = 0; i < jointInputs.getCount(); ++i)
-                {
-                    domInputLocalRef input = jointInputs.get(i);
-                    std::string inputSemantic = std::string(input->getSemantic());
-                    domURIFragmentType* sourceURI = &input->getSource();
-                    sourceURI->resolveElement();
-                    const domSourceRef source = (domSource*)(daeElement*)sourceURI->getElement();
-                    if (equals(inputSemantic, "JOINT"))
-                    {
-                        std::list<domChannelRef> channels;
-                        std::list<domNodeRef> nodes;
-                        findChannelsTargetingJoints(source, channels, nodes);
-                        // If the channels don't share the same animation then they can be grouped.
-                        if (!sameAnimation(channels))
-                        {
-                            groupPossible = true;
-                            domNode* parentMost = getCommonNodeAncestor(nodes);
-                            nodesToGroup.push_back(parentMost->getId());
-                        }
-                    }
-                }
-            }
-        }
-    }
-    return groupPossible;
-}
-
-bool sameAnimation(std::list<domChannelRef>& channels)
-{
-    std::list<domChannelRef>::iterator it = channels.begin();
-    domAnimation* temp = getAnimation(*it);
-    ++it;
-    for (; it != channels.end(); ++it)
-    {
-        if (getAnimation(*it) != temp)
-            return false;
-    }
-    return true;
-}
-
-void findChannelsTargetingJoints(const domSourceRef& source, std::list<domChannelRef>& channels, std::list<domNodeRef>& nodes)
-{
-    std::vector<std::string> jointNames;
-    getJointNames(source, jointNames);
-    for (std::vector<std::string>::iterator i = jointNames.begin(); i != jointNames.end(); ++i)
-    {
-        daeSIDResolver resolver(source->getDocument()->getDomRoot(), i->c_str());
-        daeElement* element = resolver.getElement();
-        if (element && element->typeID() == domNode::ID())
-        {
-            domNodeRef node = daeSafeCast<domNode>(element);
-            nodes.push_back(node);
-            getAnimationChannels(node, channels);
-        }
-    }
-}
-
-}

+ 0 - 203
gameplay-encoder/src/DAEUtil.h

@@ -1,203 +0,0 @@
-#ifndef DAEUTIL_H_
-#define DAEUTIL_H_
-
-namespace gameplay
-{
-
-/**
- * Gets all of the animation channels that target the given node and appends them to the list.
- * 
- * @param node The node that the animation channels target.
- * @param channels The list of channels to append to.
- */
-void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels);
-
-/**
- * Gets the joint names for the given source and appends them to the given list.
- * 
- * @param source The source element to search in.
- * @param list The list to append the joint names to.
- */
-void getJointNames(const domSourceRef source, std::vector<std::string>& list);
-
-/**
- * Gets the joint names for the given skin and appends them to the given list.
- * 
- * @param skin The skin element to search in.
- * @param list The list to append the joint names to.
- */
-void getJointNames(const domSkin* skin, std::vector<std::string>& list);
-
-/**
- * Gets the input source from the given channel.
- * 
- * @param channel The channel to search in.
- * 
- * @return The source element or NULL if not found.
- */
-domSource* getInputSource(const domChannelRef& channel);
-
-/**
- * Returns the sampler from the given channel.
- * 
- * @param channel The channel dom element.
- * 
- * @return The sampler or NULL if not found.
- */
-const domSamplerRef getSampler(const domChannelRef& channel);
-
-/**
- * Returns the source from the given sampler input.
- * Searchs within the given animation.
- * 
- * @param inputLocal The input element within a sampler.
- * @param animation The animation to search within.
- * 
- * @return The source or NULL if not found.
- */
-const domSourceRef getSource(const domInputLocalRef& inputLocal, const domAnimationRef& animation);
-
-/**
- * Returns the name array from the given source.
- * 
- * @param source The source element.
- * 
- * @return The name array or NULL if not found.
- */
-const domName_arrayRef getSourceNameArray(const domSourceRef& source);
-
-/**
- * Returns one skeleton from the given instance controller.
- * The COLLADA spec says that instance_controller can have multiple skeletons but we only need one.
- * Maya sometimes exports multiple skeleton nodes so this function will try to find the correct one.
- * 
- * @param instanceController The instance_controller element.
- * 
- * @return The skeleton or NULL if not found.
- */
-const domInstance_controller::domSkeletonRef getSkeleton(const domInstance_controllerRef& instanceController);
-
-/**
- * Returns the root joint node of the given skin.
- * 
- * @param skin The COLLADA skin to get the root joint for.
- * 
- * @return The COLLADA node or NULL if not found.
- */
-domNode* getRootJointNode(const domSkin* skin);
-
-/**
- * Returns true if the two given animation channels have equal key time input source.
- * 
- * @param c1 Channel one to compare.
- * @param c2 Channel two to compare.
- * 
- * @return True if the channels have the same key times, false otherwise.
- */
-bool equalKeyTimes(const domChannelRef& c1, const domChannelRef& c2);
-
-/**
- * Returns true if the two sources have the same key times.
- * 
- * @param s1 Source one.
- * @param s2 Source two.
- * 
- * @return True true if the key times are equal, false otherwise.
- */
-bool equalKeyTimes(const domSource* s1, const domSource* s2);
-
-/**
- * Moves a channel and the sources it uses to the destination animation.
- * 
- * @param channel The channel to move.
- * @param animation The destination animation to copy to.
- */
-void moveChannelAndSouresToAnimation(domChannelRef& channel, domAnimationRef& animation);
-
-/**
- * Returns true if the given animation is empty and contains no children.
- * 
- * @param animation The animation element to check.
- * 
- * @return True if the animation has no children, false otherwise.
- */
-bool isEmptyAnimation(domAnimationRef& animation);
-
-/**
- * Gets the visual scene from the given COLLADA dom scene.
- * 
- * @param domScene The dom scene.
- * 
- * @return The visual scene or NULL if not found.
- */
-domVisual_scene* getVisualScene(const domCOLLADA::domSceneRef& domScene);
-
-/**
- * Returns the parent node of the given node or NULL if there is no parent.
- * 
- * @param node The node to get the parent for.
- * 
- * @return The parent node or NULL if the node does not have a parent node.
- */
-domNode* getParent(domNodeRef node);
-
-/**
- * Returns the animation for the given channel.
- * 
- * @param channel The animation channel to get the animation for.
- * 
- * @return The animation of the channel or NULL if the channel does not belong to an animation.
- */
-domAnimation* getAnimation(domChannelRef channel);
-
-/**
- * Gets the common node ancestor for the given list of nodes.
- * This function assumes that the nodes share a common ancestor.
- * 
- * @param nodes The list of nodes.
- * 
- * @return The common node ancestor or NULL if the list of was empty.
- */
-domNode* getCommonNodeAncestor(std::list<domNodeRef>& nodes);
-
-/**
- * Gets the list of node ancestors for the given node.
- * 
- * @param node The node to get the ancestors for.
- * @param ancestors The output list of ancestors. 
- *                  The first element is the root node and the last element is the direct parent of the node.
- */
-void getNodeAncestors(domNodeRef& node, std::list<domNode*>& ancestors);
-
-/**
- * Finds the nodes that can be automatically grouped because there is a mesh skin that has joints 
- * that are being targetted by animations that are not grouped.
- * 
- * @param dom The COLLADA dom.
- * @param nodesToGroup The list of node IDs that can have their animations automatically grouped under.
- * 
- * @return True if there are mesh skins that can have their animations grouped, false otherwise.
- */
-bool findGroupAnimationNodes(domCOLLADA* dom, std::vector<std::string>& nodesToGroup);
-
-/**
- * Returns true if the list of animation channels share the same animation.
- * 
- * @param channels The list of channels.
- * 
- * @return True if the channels share the same animation, false otherwis.
- */ 
-bool sameAnimation(std::list<domChannelRef>& channels);
-
-/**
- * Finds the animation channels that target the given joints and the list of nodes that are targetted by those channels.
- * 
- * @param source The source element to get the list of joints from.
- * @param channels The output list of channels.
- * @param nodes The output list of nodes.
- */
-void findChannelsTargetingJoints(const domSourceRef& source, std::list<domChannelRef>& channels, std::list<domNodeRef>& nodes);
-
-}
-
-#endif

+ 0 - 183
gameplay-template/res/box.dae

@@ -1,183 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
-  <asset>
-    <contributor>
-      <author>sgrenier</author>
-      <authoring_tool>OpenCOLLADA2011 x64</authoring_tool>
-      <comments>
-			ColladaMaya export options: 
-			bakeTransforms=1;relativePaths=0;copyTextures=0;exportTriangles=1;exportCgfxFileReferences=0;
-			isSampling=0;curveConstrainSampling=0;removeStaticCurves=1;exportPolygonMeshes=1;exportLights=1;
-			exportCameras=1;exportJointsAndSkin=1;exportAnimations=1;exportInvisibleNodes=0;exportDefaultCameras=0;
-			exportTexCoords=1;exportNormals=1;exportNormalsPerVertex=1;exportVertexColors=0;exportVertexColorsPerVertex=0;
-			exportTexTangents=0;exportTangents=0;exportReferencedMaterials=0;exportMaterialsOnly=0;
-			exportXRefs=1;dereferenceXRefs=1;exportCameraAsLookat=0;cameraXFov=0;cameraYFov=1;doublePrecision=0
-		</comments>
-      <source_data>file:///C:/Users/sgrenier/Documents/maya/projects/default/untitled</source_data>
-    </contributor>
-    <created>2011-12-05T20:23:32</created>
-    <modified>2011-12-05T20:23:32</modified>
-    <unit name="centimeter" meter="0.01"/>
-    <up_axis>Y_UP</up_axis>
-  </asset>
-  <library_lights>
-    <light id="directionalLightShape" name="directionalLightShape">
-      <technique_common>
-        <directional>
-          <color>1 1 1</color>
-        </directional>
-      </technique_common>
-      <extra>
-        <technique profile="OpenCOLLADAMaya">
-          <originalMayaNodeId>directionalLightShape</originalMayaNodeId>
-        </technique>
-      </extra>
-    </light>
-  </library_lights>
-  <library_cameras>
-    <camera id="cameraShape" name="cameraShape">
-      <optics>
-        <technique_common>
-          <perspective>
-            <yfov>27.38717</yfov>
-            <aspect_ratio>1.7</aspect_ratio>
-            <znear>0.25</znear>
-            <zfar>100</zfar>
-          </perspective>
-        </technique_common>
-      </optics>
-      <extra>
-        <technique profile="OpenCOLLADAMaya">
-          <film_fit>0</film_fit>
-          <film_fit_offset>0</film_fit_offset>
-          <film_offsetX>0</film_offsetX>
-          <film_offsetY>0</film_offsetY>
-          <horizontal_aperture>4.079992</horizontal_aperture>
-          <lens_squeeze>1</lens_squeeze>
-          <originalMayaNodeId>cameraShape</originalMayaNodeId>
-          <vertical_aperture>2.399995</vertical_aperture>
-        </technique>
-      </extra>
-    </camera>
-  </library_cameras>
-  <library_materials>
-    <material id="lambert1" name="lambert1">
-      <instance_effect url="#lambert1-fx"/>
-    </material>
-  </library_materials>
-  <library_effects>
-    <effect id="lambert1-fx">
-      <profile_COMMON>
-        <technique sid="common">
-          <lambert>
-            <emission>
-              <color>0 0 0 1</color>
-            </emission>
-            <ambient>
-              <color>0 0 0 1</color>
-            </ambient>
-            <diffuse>
-              <color>0.4 0.4 0.4 1</color>
-            </diffuse>
-            <transparent opaque="RGB_ZERO">
-              <color>0 0 0 1</color>
-            </transparent>
-            <transparency>
-              <float>1</float>
-            </transparency>
-          </lambert>
-        </technique>
-      </profile_COMMON>
-    </effect>
-  </library_effects>
-  <library_geometries>
-    <geometry id="boxShape" name="boxShape">
-      <mesh>
-        <source id="boxShape-positions" name="boxShape-positions">
-          <float_array id="boxShape-positions-array" count="24">-0.5 -0.5 0.5 0.5 -0.5 0.5 -0.5 0.5 0.5 0.5 0.5 0.5 -0.5 0.5 -0.5 0.5 0.5 -0.5 -0.5 -0.5 -0.5 0.5 -0.5 -0.5</float_array>
-          <technique_common>
-            <accessor source="#boxShape-positions-array" count="8" stride="3">
-              <param name="X" type="float"/>
-              <param name="Y" type="float"/>
-              <param name="Z" type="float"/>
-            </accessor>
-          </technique_common>
-        </source>
-        <source id="boxShape-normals" name="boxShape-normals">
-          <float_array id="boxShape-normals-array" count="72">0 0 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 -1 0 0 -1 0 0 -1 0 0 -1 0 1 0 0 1 0 0 1 0 0 1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0</float_array>
-          <technique_common>
-            <accessor source="#boxShape-normals-array" count="24" stride="3">
-              <param name="X" type="float"/>
-              <param name="Y" type="float"/>
-              <param name="Z" type="float"/>
-            </accessor>
-          </technique_common>
-        </source>
-        <source id="boxShape-map1" name="boxShape-map1">
-          <float_array id="boxShape-map1-array" count="28">0.375 0 0.625 0 0.375 0.25 0.625 0.25 0.375 0.5 0.625 0.5 0.375 0.75 0.625 0.75 0.375 1 0.625 1 0.875 0 0.875 0.25 0.125 0 0.125 0.25</float_array>
-          <technique_common>
-            <accessor source="#boxShape-map1-array" count="14" stride="2">
-              <param name="S" type="float"/>
-              <param name="T" type="float"/>
-            </accessor>
-          </technique_common>
-        </source>
-        <vertices id="boxShape-vertices" name="boxShape-vertices">
-          <input semantic="POSITION" source="#boxShape-positions"/>
-        </vertices>
-        <triangles material="initialShadingGroup" count="12">
-          <input semantic="VERTEX" source="#boxShape-vertices" offset="0"/>
-          <input semantic="NORMAL" source="#boxShape-normals" offset="1"/>
-          <input semantic="TEXCOORD" source="#boxShape-map1" offset="2" set="0"/>
-          <p>0 0 0 1 1 1 2 3 2 2 3 2 1 1 1 3 2 3 2 4 2 3 5 3 4 7 4 4 7 4 3 5 3 5 6 5 4 8 4 5 9 5 6 11 6 6 11 6 5 9 5 7 10 7 6 12 6 7 13 7 0 15 8 0 15 8 7 13 7 1 14 9 1 16 1 7 17 10 3 19 3 3 19 3 7 17 10 5 18 11 6 20 12 0 21 0 4 23 13 4 23 13 0 21 0 2 22 2</p>
-        </triangles>
-      </mesh>
-      <extra>
-        <technique profile="OpenCOLLADAMaya">
-          <originalMayaNodeId>boxShape</originalMayaNodeId>
-          <double_sided>1</double_sided>
-        </technique>
-      </extra>
-    </geometry>
-  </library_geometries>
-  <library_visual_scenes>
-    <visual_scene id="VisualSceneNode" name="untitled">
-      <node id="box" name="box" type="NODE">
-        <matrix sid="transform">1 0 0 0 0 1 0 0.5 0 0 1 0 0 0 0 1</matrix>
-        <instance_geometry url="#boxShape">
-          <bind_material>
-            <technique_common>
-              <instance_material symbol="initialShadingGroup" target="#lambert1"/>
-            </technique_common>
-          </bind_material>
-        </instance_geometry>
-        <extra>
-          <technique profile="OpenCOLLADAMaya">
-            <originalMayaNodeId>box</originalMayaNodeId>
-          </technique>
-        </extra>
-      </node>
-      <node id="camera" name="camera" type="NODE">
-        <matrix sid="transform">0.9753993 -0.08127667 0.2049154 1.554299 0.0276844 0.9673549 0.251909 2.301022 -0.2187002 -0.2400389 0.9458073 7.279555 0 0 0 1</matrix>
-        <instance_camera url="#cameraShape"/>
-        <extra>
-          <technique profile="OpenCOLLADAMaya">
-            <originalMayaNodeId>camera</originalMayaNodeId>
-          </technique>
-        </extra>
-      </node>
-      <node id="directionalLight" name="directionalLight" type="NODE">
-        <matrix sid="transform">0.9282893 -0.14183 0.3437488 4.077966 -0.03090286 0.8917856 0.4514016 1.976955 -0.3705726 -0.4296541 0.8234521 6.724438 0 0 0 1</matrix>
-        <instance_light url="#directionalLightShape"/>
-        <extra>
-          <technique profile="OpenCOLLADAMaya">
-            <originalMayaNodeId>directionalLight</originalMayaNodeId>
-          </technique>
-        </extra>
-      </node>
-    </visual_scene>
-  </library_visual_scenes>
-  <scene>
-    <instance_visual_scene url="#VisualSceneNode"/>
-  </scene>
-</COLLADA>

BIN
gameplay-template/res/box.gpb


+ 2 - 2
gameplay.doxyfile

@@ -52,7 +52,7 @@ PROJECT_LOGO           =
 # If a relative path is entered, it will be relative to the location 
 # where doxygen was started. If left blank the current directory will be used.
 
-OUTPUT_DIRECTORY       = ./gameplay-api
+OUTPUT_DIRECTORY       = ./api
 
 # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
 # 4096 sub-directories (in 2 levels) under the output directory of each output 
@@ -883,7 +883,7 @@ HTML_FILE_EXTENSION    = .html
 # have to redo this when upgrading to a newer version of doxygen or when 
 # changing the value of configuration settings such as GENERATE_TREEVIEW!
 
-HTML_HEADER            = gameplay-api/header.html
+HTML_HEADER            = api/header.html
 
 # The HTML_FOOTER tag can be used to specify a personal HTML footer for 
 # each generated HTML page. If it is left blank doxygen will generate a 

+ 8 - 8
gameplay.sln

@@ -3,42 +3,42 @@ Microsoft Visual Studio Solution File, Format Version 11.00
 # Visual Studio 2010
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gameplay", "gameplay\gameplay.vcxproj", "{1032BA4B-57EB-4348-9E03-29DD63E80E4A}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gameplay-tests", "gameplay-tests\gameplay-tests.vcxproj", "{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample-browser", "samples\browser\sample-browser.vcxproj", "{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample00-mesh", "gameplay-samples\sample00-mesh\sample00-mesh.vcxproj", "{D672DC66-3CE0-4878-B0D2-813CA731012F}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample-mesh", "samples\mesh\sample-mesh.vcxproj", "{D672DC66-3CE0-4878-B0D2-813CA731012F}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample01-longboard", "gameplay-samples\sample01-longboard\sample01-longboard.vcxproj", "{9A515C8B-3320-4C5C-9754-211E91206C9D}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample-lua", "samples\lua\sample-lua.vcxproj", "{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample02-spaceship", "gameplay-samples\sample02-spaceship\sample02-spaceship.vcxproj", "{CC37B8E9-6402-4841-8D6A-5D908A5909B3}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample-longboard", "samples\longboard\sample-longboard.vcxproj", "{9A515C8B-3320-4C5C-9754-211E91206C9D}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample03-character", "gameplay-samples\sample03-character\sample03-character.vcxproj", "{87388E8B-F3CF-428F-BC2C-C1886248C111}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample-spaceship", "samples\spaceship\sample-spaceship.vcxproj", "{CC37B8E9-6402-4841-8D6A-5D908A5909B3}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample04-particles", "gameplay-samples\sample04-particles\sample04-particles.vcxproj", "{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample-character", "samples\character\sample-character.vcxproj", "{87388E8B-F3CF-428F-BC2C-C1886248C111}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample05-lua", "gameplay-samples\sample05-lua\sample05-lua.vcxproj", "{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample-particles", "samples\particles\sample-particles.vcxproj", "{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample06-racer", "gameplay-samples\sample06-racer\sample06-racer.vcxproj", "{82522888-E09A-ED48-AD7D-247237B37B3A}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample06-racer", "samples\racer\sample-racer.vcxproj", "{82522888-E09A-ED48-AD7D-247237B37B3A}"
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection

+ 0 - 30
gameplay.workspace

@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<CodeBlocks_workspace_file>
-	<Workspace title="GamePlay">
-		<Project filename="gameplay/gameplay.cbp" />
-		<Project filename="gameplay-tests/gameplay-tests.cbp">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-		<Project filename="gameplay-samples/sample00-mesh/sample00-mesh.cbp">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-		<Project filename="gameplay-samples/sample01-longboard/sample01-longboard.cbp">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-		<Project filename="gameplay-samples/sample02-spaceship/sample02-spaceship.cbp">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-		<Project filename="gameplay-samples/sample03-character/sample03-character.cbp">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-		<Project filename="gameplay-samples/sample04-particles/sample04-particles.cbp">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-		<Project filename="gameplay-samples/sample05-lua/sample05-lua.cbp">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-		<Project filename="gameplay-samples/sample06-racer/sample06-racer.cbp" active="1">
-			<Depends filename="gameplay/gameplay.cbp" />
-		</Project>
-	</Workspace>
-</CodeBlocks_workspace_file>

+ 9 - 9
gameplay.xcworkspace/contents.xcworkspacedata

@@ -5,27 +5,27 @@
       location = "group:gameplay/gameplay.xcodeproj">
    </FileRef>
    <FileRef
-      location = "group:gameplay-tests/gameplay-tests.xcodeproj">
+      location = "group:samples/browser/sample-browser.xcodeproj">
    </FileRef>
    <FileRef
-      location = "group:gameplay-samples/sample00-mesh/sample00-mesh.xcodeproj">
+      location = "group:samples/character/sample-character.xcodeproj">
    </FileRef>
    <FileRef
-      location = "group:gameplay-samples/sample01-longboard/sample01-longboard.xcodeproj">
+      location = "group:samples/longboard/sample-longboard.xcodeproj">
    </FileRef>
    <FileRef
-      location = "group:gameplay-samples/sample02-spaceship/sample02-spaceship.xcodeproj">
-   </FileRef>
+      location = "group:samples/lua/sample-lua.xcodeproj">
+   </FileRef>  
    <FileRef
-      location = "group:gameplay-samples/sample03-character/sample03-character.xcodeproj">
+      location = "group:samples/mesh/sample-mesh.xcodeproj">
    </FileRef>
    <FileRef
-      location = "group:gameplay-samples/sample04-particles/sample04-particles.xcodeproj">
+      location = "group:samples/particles/sample-particles.xcodeproj">
    </FileRef>
    <FileRef
-      location = "group:gameplay-samples/sample05-lua/sample05-lua.xcodeproj">
+      location = "group:samples/racer/sample-racer.xcodeproj">
    </FileRef>
    <FileRef
-      location = "group:gameplay-samples/sample06-racer/sample06-racer.xcodeproj">
+      location = "group:samples/spaceship/sample-spaceship.xcodeproj">
    </FileRef>
 </Workspace>

+ 7 - 0
gameplay/CMakeLists.txt

@@ -83,6 +83,8 @@ set(GAMEPLAY_SRC
     src/Image.cpp
     src/Image.h
     src/Image.inl
+    src/ImageControl.cpp
+    src/ImageControl.h
     src/Joint.cpp
     src/Joint.h
     src/Joystick.cpp
@@ -156,6 +158,7 @@ set(GAMEPLAY_SRC
     src/Plane.h
     src/Plane.inl
     src/Platform.h
+    src/Platform.cpp
     src/PlatformAndroid.cpp
     src/PlatformBlackBerry.cpp
     src/PlatformLinux.cpp
@@ -349,6 +352,8 @@ set(GAMEPLAY_LUA
     src/lua/lua_HeightField.h
     src/lua/lua_Image.cpp
     src/lua/lua_Image.h
+    src/lua/lua_ImageControl.cpp
+    src/lua/lua_ImageControl.h
     src/lua/lua_ImageFormat.cpp
     src/lua/lua_ImageFormat.h
     src/lua/lua_Joint.cpp
@@ -513,6 +518,8 @@ set(GAMEPLAY_LUA
     src/lua/lua_Terrain.h
     src/lua/lua_TerrainFlags.cpp
     src/lua/lua_TerrainFlags.h
+    src/lua/lua_TerrainListener.cpp
+    src/lua/lua_TerrainListener.h
     src/lua/lua_TextBox.cpp
     src/lua/lua_TextBox.h
     src/lua/lua_Texture.cpp

+ 2 - 5
gameplay/android/build.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project name="gameplay-android" default="help">
+<project name="gameplay" 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
@@ -40,10 +40,7 @@
     <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"
-    />
+    <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

+ 5 - 14
gameplay/android/jni/Android.mk

@@ -1,17 +1,4 @@
-# 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)
@@ -54,6 +41,7 @@ LOCAL_SRC_FILES := \
     Gamepad.cpp \
     HeightField.cpp \
     Image.cpp \
+	ImageControl.cpp \
     Joint.cpp \
     Joystick.cpp \
     Label.cpp \
@@ -87,6 +75,7 @@ LOCAL_SRC_FILES := \
     PhysicsVehicle.cpp \
     PhysicsVehicleWheel.cpp \
     Plane.cpp \
+    Platform.cpp \
     PlatformAndroid.cpp \
     Properties.cpp \
     Quaternion.cpp \
@@ -178,6 +167,7 @@ LOCAL_SRC_FILES := \
     lua/lua_Global.cpp \
     lua/lua_HeightField.cpp \
     lua/lua_Image.cpp \
+    lua/lua_ImageControl.cpp \
     lua/lua_ImageFormat.cpp \
     lua/lua_Joint.cpp \
     lua/lua_Joystick.cpp \
@@ -260,6 +250,7 @@ LOCAL_SRC_FILES := \
     lua/lua_Technique.cpp \
     lua/lua_Terrain.cpp \
     lua/lua_TerrainFlags.cpp \
+    lua/lua_TerrainListener.cpp \
     lua/lua_TextBox.cpp \
     lua/lua_Texture.cpp \
     lua/lua_TextureFilter.cpp \

+ 22 - 9
gameplay/gameplay.vcxproj

@@ -91,6 +91,7 @@
     <ClCompile Include="src\gameplay-main-windows.cpp" />
     <ClCompile Include="src\HeightField.cpp" />
     <ClCompile Include="src\Image.cpp" />
+    <ClCompile Include="src\ImageControl.cpp" />
     <ClCompile Include="src\Joint.cpp" />
     <ClCompile Include="src\Joystick.cpp" />
     <ClCompile Include="src\Label.cpp" />
@@ -158,6 +159,7 @@
     <ClCompile Include="src\lua\lua_Global.cpp" />
     <ClCompile Include="src\lua\lua_HeightField.cpp" />
     <ClCompile Include="src\lua\lua_Image.cpp" />
+    <ClCompile Include="src\lua\lua_ImageControl.cpp" />
     <ClCompile Include="src\lua\lua_ImageFormat.cpp" />
     <ClCompile Include="src\lua\lua_Joint.cpp" />
     <ClCompile Include="src\lua\lua_Joystick.cpp" />
@@ -240,6 +242,7 @@
     <ClCompile Include="src\lua\lua_Technique.cpp" />
     <ClCompile Include="src\lua\lua_Terrain.cpp" />
     <ClCompile Include="src\lua\lua_TerrainFlags.cpp" />
+    <ClCompile Include="src\lua\lua_TerrainListener.cpp" />
     <ClCompile Include="src\lua\lua_TextBox.cpp" />
     <ClCompile Include="src\lua\lua_Texture.cpp" />
     <ClCompile Include="src\lua\lua_TextureFilter.cpp" />
@@ -292,6 +295,7 @@
     <ClCompile Include="src\PhysicsVehicle.cpp" />
     <ClCompile Include="src\PhysicsVehicleWheel.cpp" />
     <ClCompile Include="src\Plane.cpp" />
+    <ClCompile Include="src\Platform.cpp" />
     <ClCompile Include="src\PlatformAndroid.cpp" />
     <ClCompile Include="src\PlatformBlackBerry.cpp" />
     <ClCompile Include="src\PlatformLinux.cpp" />
@@ -366,6 +370,7 @@
     <ClInclude Include="src\Gesture.h" />
     <ClInclude Include="src\HeightField.h" />
     <ClInclude Include="src\Image.h" />
+    <ClInclude Include="src\ImageControl.h" />
     <ClInclude Include="src\Joint.h" />
     <ClInclude Include="src\Joystick.h" />
     <ClInclude Include="src\Keyboard.h" />
@@ -434,6 +439,7 @@
     <ClInclude Include="src\lua\lua_Global.h" />
     <ClInclude Include="src\lua\lua_HeightField.h" />
     <ClInclude Include="src\lua\lua_Image.h" />
+    <ClInclude Include="src\lua\lua_ImageControl.h" />
     <ClInclude Include="src\lua\lua_ImageFormat.h" />
     <ClInclude Include="src\lua\lua_Joint.h" />
     <ClInclude Include="src\lua\lua_Joystick.h" />
@@ -516,6 +522,7 @@
     <ClInclude Include="src\lua\lua_Technique.h" />
     <ClInclude Include="src\lua\lua_Terrain.h" />
     <ClInclude Include="src\lua\lua_TerrainFlags.h" />
+    <ClInclude Include="src\lua\lua_TerrainListener.h" />
     <ClInclude Include="src\lua\lua_TextBox.h" />
     <ClInclude Include="src\lua\lua_Texture.h" />
     <ClInclude Include="src\lua\lua_TextureFilter.h" />
@@ -612,15 +619,21 @@
     <None Include="res\shaders\colored-unlit.vert" />
     <None Include="res\shaders\colored.frag" />
     <None Include="res\shaders\colored.vert" />
-    <None Include="res\shaders\lib\attributes-skinning.vert" />
-    <None Include="res\shaders\lib\attributes.vert" />
-    <None Include="res\shaders\lib\lighting-directional.frag" />
-    <None Include="res\shaders\lib\lighting-directional.vert" />
-    <None Include="res\shaders\lib\lighting-point.frag" />
-    <None Include="res\shaders\lib\lighting-point.vert" />
-    <None Include="res\shaders\lib\lighting-spot.frag" />
-    <None Include="res\shaders\lib\lighting-spot.vert" />
-    <None Include="res\shaders\lib\lighting.frag" />
+    <None Include="res\shaders\font.frag" />
+    <None Include="res\shaders\font.vert" />
+    <None Include="res\shaders\form.frag" />
+    <None Include="res\shaders\form.vert" />
+    <None Include="res\shaders\lighting-directional.frag" />
+    <None Include="res\shaders\lighting-directional.vert" />
+    <None Include="res\shaders\lighting-point.frag" />
+    <None Include="res\shaders\lighting-point.vert" />
+    <None Include="res\shaders\lighting-spot.frag" />
+    <None Include="res\shaders\lighting-spot.vert" />
+    <None Include="res\shaders\lighting.frag" />
+    <None Include="res\shaders\skinning-none.vert" />
+    <None Include="res\shaders\skinning.vert" />
+    <None Include="res\shaders\sprite.frag" />
+    <None Include="res\shaders\sprite.vert" />
     <None Include="res\shaders\terrain.frag" />
     <None Include="res\shaders\terrain.vert" />
     <None Include="res\shaders\textured-bumped.frag" />

+ 64 - 28
gameplay/gameplay.vcxproj.filters

@@ -10,9 +10,6 @@
     <Filter Include="res\shaders">
       <UniqueIdentifier>{be0b36f1-49ed-4a06-9f1f-57c654a554fe}</UniqueIdentifier>
     </Filter>
-    <Filter Include="res\shaders\lib">
-      <UniqueIdentifier>{d42defb1-22e2-4573-8077-9bd23e61494c}</UniqueIdentifier>
-    </Filter>
     <Filter Include="src\lua">
       <UniqueIdentifier>{21cf31c6-9c10-44cb-a864-d46a0e7bfe5e}</UniqueIdentifier>
     </Filter>
@@ -840,6 +837,18 @@
     <ClCompile Include="src\lua\lua_HeightField.cpp">
       <Filter>src\lua</Filter>
     </ClCompile>
+    <ClCompile Include="src\Platform.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\ImageControl.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\lua\lua_ImageControl.cpp">
+      <Filter>src\lua</Filter>
+    </ClCompile>
+    <ClCompile Include="src\lua\lua_TerrainListener.cpp">
+      <Filter>src\lua</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -1667,6 +1676,15 @@
     <ClInclude Include="src\lua\lua_HeightField.h">
       <Filter>src\lua</Filter>
     </ClInclude>
+    <ClInclude Include="src\ImageControl.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\lua\lua_ImageControl.h">
+      <Filter>src\lua</Filter>
+    </ClInclude>
+    <ClInclude Include="src\lua\lua_TerrainListener.h">
+      <Filter>src\lua</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\Game.inl">
@@ -1714,55 +1732,73 @@
     <None Include="res\shaders\colored-unlit.vert">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\textured.frag">
+    <None Include="res\shaders\font.frag">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\textured.vert">
+    <None Include="res\shaders\font.vert">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\textured-bumped.frag">
+    <None Include="res\shaders\form.frag">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\textured-bumped.vert">
+    <None Include="res\shaders\form.vert">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\textured-unlit.frag">
+    <None Include="res\shaders\lighting.frag">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\textured-unlit.vert">
+    <None Include="res\shaders\lighting-directional.frag">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\lighting-directional.vert">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\attributes.vert">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\lighting-point.frag">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\attributes-skinning.vert">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\lighting-point.vert">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\lighting.frag">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\lighting-spot.frag">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\lighting-directional.frag">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\lighting-spot.vert">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\lighting-directional.vert">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\skinning.vert">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\lighting-point.frag">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\skinning-none.vert">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\lighting-point.vert">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\sprite.frag">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\lighting-spot.frag">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\sprite.vert">
+      <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\lib\lighting-spot.vert">
-      <Filter>res\shaders\lib</Filter>
+    <None Include="res\shaders\terrain.frag">
+      <Filter>res\shaders</Filter>
     </None>
     <None Include="res\shaders\terrain.vert">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\terrain.frag">
+    <None Include="res\shaders\textured.frag">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\textured.vert">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\textured-bumped.frag">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\textured-bumped.vert">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\textured-unlit.frag">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\textured-unlit.vert">
       <Filter>res\shaders</Filter>
     </None>
   </ItemGroup>

File diff suppressed because it is too large
+ 431 - 426
gameplay/gameplay.xcodeproj/project.pbxproj


+ 7 - 0
gameplay/gameplay.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:gameplay.xcodeproj">
+   </FileRef>
+</Workspace>

BIN
gameplay/gameplay.xcodeproj/project.xcworkspace/xcuserdata/mozeal.xcuserdatad/UserInterfaceState.xcuserstate


+ 1 - 1
gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-ios.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0450"
+   LastUpgradeVersion = "0460"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-macosx.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0450"
+   LastUpgradeVersion = "0460"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

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

@@ -15,11 +15,11 @@ varying vec3 v_color;										// Output Vertex Color
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;					// Matrix to transform a position to clip space.
 uniform mat4 u_inverseTransposeWorldViewMatrix;				// Matrix to transform a normal to view space
+uniform mat4 u_worldViewMatrix;								// Matrix to tranform a position to view space.
 #if defined(SKINNING)
 uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
 #endif
 #if defined(SPECULAR)
-uniform mat4 u_worldViewMatrix;								// Matrix to tranform a position to view space.
 uniform vec3 u_cameraPosition;                 				// Position of the camera in view space.
 #endif
 #if defined(POINT_LIGHT)

+ 12 - 1
gameplay/src/AIStateMachine.cpp

@@ -25,7 +25,18 @@ AIStateMachine::~AIStateMachine()
     {
         (*itr)->release();
     }
-    SAFE_RELEASE(AIState::_empty);
+
+    if (AIState::_empty)
+    {
+        if (AIState::_empty->getRefCount() == 1)
+        {
+            SAFE_RELEASE(AIState::_empty);
+        }
+        else
+        {
+            AIState::_empty->release();
+        }
+    }
 }
 
 AIAgent* AIStateMachine::getAgent() const

+ 1 - 1
gameplay/src/AbsoluteLayout.cpp

@@ -41,7 +41,7 @@ void AbsoluteLayout::update(const Container* container, const Vector2& offset)
     GP_ASSERT(container);
 
     // An AbsoluteLayout does nothing to modify the layout of Controls.
-    std::vector<Control*> controls = container->getControls();
+    const std::vector<Control*>& controls = container->getControls();
     for (size_t i = 0, count = controls.size(); i < count; i++)
     {
         Control* control = controls[i];

+ 3 - 1
gameplay/src/Animation.cpp

@@ -254,7 +254,7 @@ void Animation::createClips(Properties* animationProperties, unsigned int frameC
         int begin = pClip->getInt("begin");
         int end = pClip->getInt("end");
 
-        AnimationClip* clip = createClip(pClip->getId(), ((float) begin / frameCount) * _duration, ((float) end / frameCount) * _duration);
+        AnimationClip* clip = createClip(pClip->getId(), ((float)begin / (frameCount-1)) * _duration, ((float)end / (frameCount-1)) * _duration);
 
         const char* repeat = pClip->getString("repeatCount");
         if (repeat)
@@ -279,6 +279,8 @@ void Animation::createClips(Properties* animationProperties, unsigned int frameC
             clip->setSpeed(value);
         }
 
+        clip->setLoopBlendTime(pClip->getFloat("loopBlendTime")); // returns zero if not specified
+
         pClip = animationProperties->getNextNamespace();
     }
 }

+ 69 - 36
gameplay/src/AnimationClip.cpp

@@ -11,7 +11,7 @@ namespace gameplay
 
 AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime)
     : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _duration(_endTime - _startTime), 
-      _stateBits(0x00), _repeatCount(1.0f), _activeDuration(_duration * _repeatCount), _speed(1.0f), _timeStarted(0), 
+      _stateBits(0x00), _repeatCount(1.0f), _loopBlendTime(0), _activeDuration(_duration * _repeatCount), _speed(1.0f), _timeStarted(0), 
       _elapsedTime(0), _crossFadeToClip(NULL), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), 
       _beginListeners(NULL), _endListeners(NULL), _listeners(NULL), _listenerItr(NULL), _scriptListeners(NULL)
 {
@@ -106,11 +106,14 @@ void AnimationClip::setRepeatCount(float repeatCount)
 
     if (repeatCount == REPEAT_INDEFINITE)
     {
-        _activeDuration = _duration;
+        _activeDuration = _duration + _loopBlendTime;
     }
     else
     {
-        _activeDuration = _repeatCount * _duration;
+        _activeDuration = _duration * _repeatCount;
+
+        if (repeatCount > 1.0f && _loopBlendTime > 0.0f)
+            _activeDuration += std::ceil(repeatCount - 1.0f) * _loopBlendTime;
     }
 }
 
@@ -121,14 +124,15 @@ float AnimationClip::getRepeatCount() const
 
 void AnimationClip::setActiveDuration(unsigned long duration)
 {
+    GP_ASSERT(duration > 0.0f);
+
     if (duration == REPEAT_INDEFINITE)
     {
-        _repeatCount = REPEAT_INDEFINITE;
-        _activeDuration = _duration;
+        _activeDuration = _duration + _loopBlendTime;
     }
     else
     {
-        _activeDuration = _duration;
+        _activeDuration = duration;
         _repeatCount = (float)_activeDuration / (float)_duration;
     }
 }
@@ -166,6 +170,18 @@ float AnimationClip::getBlendWeight() const
     return _blendWeight;
 }
 
+void AnimationClip::setLoopBlendTime(float loopBlendTime)
+{
+    _loopBlendTime = loopBlendTime;
+    if (_loopBlendTime < 0.0f)
+        _loopBlendTime = 0.0f;
+}
+
+float AnimationClip::getLoopBlendTime() const
+{
+    return _loopBlendTime;
+}
+
 bool AnimationClip::isPlaying() const
 {
     return (isClipStateBitSet(CLIP_IS_PLAYING_BIT) && !isClipStateBitSet(CLIP_IS_PAUSED_BIT));
@@ -358,57 +374,59 @@ bool AnimationClip::update(float elapsedTime)
     {
         return false;
     }
-    else if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
-    {   // If the marked for removal bit is set, it means stop() was called on the AnimationClip at some point
+
+    if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+    {
+        // If the marked for removal bit is set, it means stop() was called on the AnimationClip at some point
         // after the last update call. Reset the flag, and return true so the AnimationClip is removed from the 
         // running clips on the AnimationController.
         onEnd();
         return true;
     }
-    else if (!isClipStateBitSet(CLIP_IS_STARTED_BIT))
+
+    if (!isClipStateBitSet(CLIP_IS_STARTED_BIT))
     {
+        // Clip is just starting
         onBegin();
     }
     else
     {
+        // Clip was already running
         _elapsedTime += elapsedTime * _speed;
 
         if (_repeatCount == REPEAT_INDEFINITE && _elapsedTime <= 0)
+        {
+            // Elapsed time is moving backwards, so wrap it back around the end when it falls below zero
             _elapsedTime = _activeDuration + _elapsedTime;
+
+            // TODO: account for _loopBlendTime
+        }
     }
 
+    // Current time within a loop of the clip
     float currentTime = 0.0f;
+
     // Check to see if clip is complete.
     if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0.0f)))
     {
+        // We finished our active duration (including repeats), so clamp to our end value.
         resetClipStateBit(CLIP_IS_STARTED_BIT);
-        
-        if (_speed >= 0.0f)
-        {
-            // If _duration == 0, we have a "pose". Just set currentTime to 0.
-            if (_duration == 0)
-            {
-                currentTime = 0.0f;
-            }
-            else
-            {
-                currentTime = (float)(_activeDuration % _duration); // Get's the fractional part of the final repeat.
-                if (currentTime == 0.0f)
-                    currentTime = _duration;
-            }
-        }
-        else
-        {
-            currentTime = 0.0f; // If we are negative speed, the end value should be 0.
-        }
+
+        // Ensure we end off at the endpoints of our clip (-speed==0, +speed==_duration)
+        currentTime = _speed < 0.0f ? 0.0f : _duration;
     }
     else
     {
         // If _duration == 0, we have a "pose". Just set currentTime to 0.
         if (_duration == 0)
+        {
             currentTime = 0.0f;
-        else // Gets portion/fraction of the repeat.
-            currentTime = fmodf(_elapsedTime, _duration);
+        }
+        else
+        {
+            // Animation is running normally.
+            currentTime = fmodf(_elapsedTime, _duration + _loopBlendTime);
+        }
     }
 
     // Notify any listeners of Animation events.
@@ -445,12 +463,15 @@ bool AnimationClip::update(float elapsedTime)
     // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
     GP_ASSERT(_animation);
 
-    // If the animation duration is zero (start time == end time, such as when there is only a single keyframe),
-    // then prevent a divide by zero and set percentComplete = 1.
-    float percentComplete = _animation->_duration == 0 ? 1 : ((float)_startTime + currentTime) / (float)_animation->_duration;
-    
-    percentComplete = MATH_CLAMP(percentComplete, 0.0f, 1.0f);
+    // Compute percentage complete for the current loop (prevent a divide by zero if _duration==0).
+    // Note that we don't use (currentTime/(_duration+_loopBlendTime)). That's because we want a
+    // % value that is outside the 0-1 range for loop smoothing/blending purposes.
+    float percentComplete = _duration == 0 ? 1 : currentTime / (float)_duration;
 
+    if (_loopBlendTime == 0.0f)
+        percentComplete = MATH_CLAMP(percentComplete, 0.0f, 1.0f);
+
+    // If we're cross fading, compute blend weights
     if (isClipStateBitSet(CLIP_IS_FADING_OUT_BIT))
     {
         GP_ASSERT(_crossFadeToClip);
@@ -503,6 +524,9 @@ bool AnimationClip::update(float elapsedTime)
     AnimationValue* value = NULL;
     AnimationTarget* target = NULL;
     size_t channelCount = _animation->_channels.size();
+    float percentageStart = (float)_startTime / (float)_animation->_duration;
+    float percentageEnd = (float)_endTime / (float)_animation->_duration;
+    float percentageBlend = (float)_loopBlendTime / (float)_animation->_duration;
     for (size_t i = 0; i < channelCount; i++)
     {
         channel = _animation->_channels[i];
@@ -514,7 +538,8 @@ bool AnimationClip::update(float elapsedTime)
 
         // Evaluate the point on Curve
         GP_ASSERT(channel->getCurve());
-        channel->getCurve()->evaluate(percentComplete, value->_value);
+        channel->getCurve()->evaluate(percentComplete, percentageStart, percentageEnd, percentageBlend, value->_value);
+
         // Set the animation value on the target property.
         target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
     }
@@ -531,6 +556,8 @@ bool AnimationClip::update(float elapsedTime)
 
 void AnimationClip::onBegin()
 {
+    addRef();
+
     // Initialize animation to play.
     setClipStateBit(CLIP_IS_STARTED_BIT);
     if (_speed >= 0)
@@ -559,10 +586,14 @@ void AnimationClip::onBegin()
             listener++;
         }
     }
+
+    release();
 }
 
 void AnimationClip::onEnd()
 {
+    addRef();
+
     _blendWeight = 1.0f;
     resetClipStateBit(CLIP_ALL_BITS);
 
@@ -577,6 +608,8 @@ void AnimationClip::onEnd()
             listener++;
         }
     }
+
+    release();
 }
 
 bool AnimationClip::isClipStateBitSet(unsigned char bit) const

+ 17 - 0
gameplay/src/AnimationClip.h

@@ -178,6 +178,22 @@ public:
      */
     float getBlendWeight() const;
 
+    /**
+     * Sets the time (in milliseconds) to append to the clip's active duration
+     * to use for blending the end points of the clip when looping.
+     *
+     * @param loopBlendTime Time spent blending end points of clip when looping.
+     */
+    void setLoopBlendTime(float loopBlendTime);
+
+    /**
+     * Returns the amount of time (in milliseconds) spent blending the clip's 
+     * end points when looping.
+     *
+     * @return Time spent blending end points of the clip when looping.
+     */
+    float getLoopBlendTime() const;
+
     /**
      * Checks if the AnimationClip is playing.
      *
@@ -392,6 +408,7 @@ private:
     unsigned long _duration;                            // The total duration.
     unsigned char _stateBits;                           // Bit flag used to keep track of the clip's current state.
     float _repeatCount;                                 // The clip's repeat count.
+    unsigned int _loopBlendTime;                        // Time spent blending the last frame of animation with the first frame, when looping.
     unsigned long _activeDuration;                      // The active duration of the clip.
     float _speed;                                       // The speed that the clip is playing. Default is 1.0. Negative goes in reverse.
     double _timeStarted;                                // The game time when this clip was actually started.

+ 3 - 1
gameplay/src/AnimationController.cpp

@@ -106,6 +106,7 @@ void AnimationController::update(float elapsedTime)
     {
         AnimationClip* clip = (*clipIter);
         GP_ASSERT(clip);
+        clip->addRef();
         if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
         {   // If the CLIP_IS_RESTARTED_BIT is set, we should end the clip and 
             // move it from where it is in the running clips list to the back.
@@ -116,13 +117,14 @@ void AnimationController::update(float elapsedTime)
         }
         else if (clip->update(elapsedTime))
         {
-            SAFE_RELEASE(clip);
+            clip->release();
             clipIter = _runningClips.erase(clipIter);
         }
         else
         {
             clipIter++;
         }
+        clip->release();
     }
 
     Transform::resumeTransformChanged();

+ 37 - 7
gameplay/src/Button.cpp

@@ -50,11 +50,8 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
                 _contactIndex = (int) contactIndex;
-
                 setState(Control::ACTIVE);
-
-                notifyListeners(Listener::PRESS);
-
+                notifyListeners(Control::Listener::PRESS);
                 return _consumeInputEvents;
             }
             else
@@ -68,14 +65,13 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         if (_contactIndex == (int) contactIndex)
         {
             _contactIndex = INVALID_CONTACT_INDEX;
-            notifyListeners(Listener::RELEASE);
+            notifyListeners(Control::Listener::RELEASE);
             if (!_parent->isScrolling() &&
                 x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
                 setState(Control::FOCUS);
-
-                notifyListeners(Listener::CLICK);
+                notifyListeners(Control::Listener::CLICK);
             }
             else
             {
@@ -93,6 +89,40 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return false;
 }
 
+bool Button::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::FOCUS)
+        {
+            if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+                gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::PRESS);
+                setState(Control::ACTIVE);
+                return _consumeInputEvents;
+            }
+        }
+        else if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::RELEASE);
+                notifyListeners(Control::Listener::CLICK);
+                setState(Control::FOCUS);
+                return _consumeInputEvents;
+            }
+        }
+        break;
+    default:
+        break;
+    }
+
+    return false;
+}
+
 const char* Button::getType() const
 {
     return "button";

+ 7 - 0
gameplay/src/Button.h

@@ -85,6 +85,13 @@ protected:
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * @see Control::getType
      */

+ 41 - 22
gameplay/src/Camera.cpp

@@ -14,18 +14,21 @@
 #define CAMERA_DIRTY_BOUNDS 32
 #define CAMERA_DIRTY_ALL (CAMERA_DIRTY_VIEW | CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS)
 
+// Other misc camera bits
+#define CAMERA_CUSTOM_PROJECTION 64
+
 namespace gameplay
 {
 
 Camera::Camera(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
     : _type(PERSPECTIVE), _fieldOfView(fieldOfView), _aspectRatio(aspectRatio), _nearPlane(nearPlane), _farPlane(farPlane),
-      _dirtyBits(CAMERA_DIRTY_ALL), _node(NULL)
+      _bits(CAMERA_DIRTY_ALL), _node(NULL)
 {
 }
 
 Camera::Camera(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane)
     : _type(ORTHOGRAPHIC), _aspectRatio(aspectRatio), _nearPlane(nearPlane), _farPlane(farPlane),
-      _dirtyBits(CAMERA_DIRTY_ALL), _node(NULL)
+      _bits(CAMERA_DIRTY_ALL), _node(NULL)
 {
     // Orthographic camera.
     _zoom[0] = zoomX;
@@ -131,7 +134,7 @@ void Camera::setFieldOfView(float fieldOfView)
     GP_ASSERT(_type == Camera::PERSPECTIVE);
 
     _fieldOfView = fieldOfView;
-    _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
 }
 
 float Camera::getZoomX() const
@@ -146,7 +149,7 @@ void Camera::setZoomX(float zoomX)
     GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
     _zoom[0] = zoomX;
-    _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
 }
 
 float Camera::getZoomY() const
@@ -161,7 +164,7 @@ void Camera::setZoomY(float zoomY)
     GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
     _zoom[1] = zoomY;
-    _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
 }
 
 float Camera::getAspectRatio() const
@@ -172,7 +175,7 @@ float Camera::getAspectRatio() const
 void Camera::setAspectRatio(float aspectRatio)
 {
     _aspectRatio = aspectRatio;
-    _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
 }
 
 float Camera::getNearPlane() const
@@ -183,7 +186,7 @@ float Camera::getNearPlane() const
 void Camera::setNearPlane(float nearPlane)
 {
     _nearPlane = nearPlane;
-    _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
 }
 
 float Camera::getFarPlane() const
@@ -194,7 +197,7 @@ float Camera::getFarPlane() const
 void Camera::setFarPlane(float farPlane)
 {
     _farPlane = farPlane;
-    _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
 }
 
 Node* Camera::getNode() const
@@ -219,13 +222,13 @@ void Camera::setNode(Node* node)
             _node->addListener(this);
         }
 
-        _dirtyBits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+        _bits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
     }
 }
 
 const Matrix& Camera::getViewMatrix() const
 {
-    if (_dirtyBits & CAMERA_DIRTY_VIEW)
+    if (_bits & CAMERA_DIRTY_VIEW)
     {
         if (_node)
         {
@@ -237,7 +240,7 @@ const Matrix& Camera::getViewMatrix() const
             _view.setIdentity();
         }
 
-        _dirtyBits &= ~CAMERA_DIRTY_VIEW;
+        _bits &= ~CAMERA_DIRTY_VIEW;
     }
 
     return _view;
@@ -245,11 +248,11 @@ const Matrix& Camera::getViewMatrix() const
 
 const Matrix& Camera::getInverseViewMatrix() const
 {
-    if (_dirtyBits & CAMERA_DIRTY_INV_VIEW)
+    if (_bits & CAMERA_DIRTY_INV_VIEW)
     {
         getViewMatrix().invert(&_inverseView);
 
-        _dirtyBits &= ~CAMERA_DIRTY_INV_VIEW;
+        _bits &= ~CAMERA_DIRTY_INV_VIEW;
     }
 
     return _inverseView;
@@ -257,7 +260,7 @@ const Matrix& Camera::getInverseViewMatrix() const
 
 const Matrix& Camera::getProjectionMatrix() const
 {
-    if (_dirtyBits & CAMERA_DIRTY_PROJ)
+    if (!(_bits & CAMERA_CUSTOM_PROJECTION) && (_bits & CAMERA_DIRTY_PROJ))
     {
         if (_type == PERSPECTIVE)
         {
@@ -268,19 +271,35 @@ const Matrix& Camera::getProjectionMatrix() const
             Matrix::createOrthographic(_zoom[0], _zoom[1], _nearPlane, _farPlane, &_projection);
         }
 
-        _dirtyBits &= ~CAMERA_DIRTY_PROJ;
+        _bits &= ~CAMERA_DIRTY_PROJ;
     }
 
     return _projection;
 }
 
+void Camera::setProjectionMatrix(const Matrix& matrix)
+{
+    _projection = matrix;
+    _bits |= CAMERA_CUSTOM_PROJECTION;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+}
+
+void Camera::resetProjectionMatrix()
+{
+    if (_bits & CAMERA_CUSTOM_PROJECTION)
+    {
+        _bits &= ~CAMERA_CUSTOM_PROJECTION;
+        _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    }
+}
+
 const Matrix& Camera::getViewProjectionMatrix() const
 {
-    if (_dirtyBits & CAMERA_DIRTY_VIEW_PROJ)
+    if (_bits & CAMERA_DIRTY_VIEW_PROJ)
     {
         Matrix::multiply(getProjectionMatrix(), getViewMatrix(), &_viewProjection);
 
-        _dirtyBits &= ~CAMERA_DIRTY_VIEW_PROJ;
+        _bits &= ~CAMERA_DIRTY_VIEW_PROJ;
     }
 
     return _viewProjection;
@@ -288,11 +307,11 @@ const Matrix& Camera::getViewProjectionMatrix() const
 
 const Matrix& Camera::getInverseViewProjectionMatrix() const
 {
-    if (_dirtyBits & CAMERA_DIRTY_INV_VIEW_PROJ)
+    if (_bits & CAMERA_DIRTY_INV_VIEW_PROJ)
     {
         getViewProjectionMatrix().invert(&_inverseViewProjection);
 
-        _dirtyBits &= ~CAMERA_DIRTY_INV_VIEW_PROJ;
+        _bits &= ~CAMERA_DIRTY_INV_VIEW_PROJ;
     }
 
     return _inverseViewProjection;
@@ -300,12 +319,12 @@ const Matrix& Camera::getInverseViewProjectionMatrix() const
 
 const Frustum& Camera::getFrustum() const
 {
-    if (_dirtyBits & CAMERA_DIRTY_BOUNDS)
+    if (_bits & CAMERA_DIRTY_BOUNDS)
     {
         // Update our bounding frustum from our view projection matrix.
         _bounds.set(getViewProjectionMatrix());
 
-        _dirtyBits &= ~CAMERA_DIRTY_BOUNDS;
+        _bits &= ~CAMERA_DIRTY_BOUNDS;
     }
 
     return _bounds;
@@ -404,7 +423,7 @@ Camera* Camera::clone(NodeCloneContext &context) const
 
 void Camera::transformChanged(Transform* transform, long cookie)
 {
-    _dirtyBits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    _bits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
 }
 
 }

+ 21 - 1
gameplay/src/Camera.h

@@ -191,6 +191,26 @@ public:
      */
     const Matrix& getProjectionMatrix() const;
 
+    /**
+     * Sets a custom projection matrix to be used by the camera.
+     *
+     * Setting a custom projection matrix results in the internally 
+     * computed projection matrix being completely overriden until
+     * the resetProjectionMatrix method is called. A custom projection
+     * matrix is normally not neccessary, but can be used for special
+     * projection effects, such as setting an oblique view frustum
+     * for near plane clipping.
+     *
+     * @param matrix Custom projection matrix.
+     */
+    void setProjectionMatrix(const Matrix& matrix);
+
+    /**
+     * Resets the camera to use the internally computed projection matrix
+     * instead of any previously specified user-defined matrix.
+     */
+    void resetProjectionMatrix();
+
     /**
      * Gets the camera's view * projection matrix.
      *
@@ -299,7 +319,7 @@ private:
     mutable Matrix _inverseView;
     mutable Matrix _inverseViewProjection;
     mutable Frustum _bounds;
-    mutable int _dirtyBits;
+    mutable int _bits;
     Node* _node;
 };
 

+ 20 - 0
gameplay/src/CheckBox.cpp

@@ -94,6 +94,26 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
     return Button::touchEvent(evt, x, y, contactIndex);
 }
 
+bool CheckBox::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                _checked = !_checked;
+                notifyListeners(Control::Listener::VALUE_CHANGED);   
+            }
+        }
+        break;
+    }
+
+    return Button::gamepadEvent(evt, gamepad, analogIndex);
+}
+
 void CheckBox::update(const Control* container, const Vector2& offset)
 {
     Label::update(container, offset);

+ 7 - 0
gameplay/src/CheckBox.h

@@ -131,6 +131,13 @@ protected:
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.

+ 562 - 51
gameplay/src/Container.cpp

@@ -11,6 +11,7 @@
 #include "Slider.h"
 #include "TextBox.h"
 #include "Joystick.h"
+#include "ImageControl.h"
 #include "Game.h"
 
 namespace gameplay
@@ -22,6 +23,12 @@ static const long SCROLL_INERTIA_DELAY = 100L;
 static const float SCROLL_FRICTION_FACTOR = 5.0f;
 // Distance that must be scrolled before isScrolling() will return true, used e.g. to cancel button-click events.
 static const float SCROLL_THRESHOLD = 10.0f;
+// Distance a joystick must be pushed in order to trigger focus-change and/or scrolling.
+static const float JOYSTICK_THRESHOLD = 0.75f;
+// Scroll speed when using a DPad -- max scroll speed when using a joystick.
+static const float GAMEPAD_SCROLL_SPEED = 500.0f;
+// If the DPad or joystick is held down, this is the initial delay in milliseconds between focus change events.
+static const float FOCUS_CHANGE_REPEAT_DELAY = 300.0f;
 
 /**
  * Sort function for use with _controls.sort(), based on Z-Order.
@@ -42,7 +49,11 @@ Container::Container()
       _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
       _scrollingRight(false), _scrollingDown(false),
       _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
-      _scrollBarOpacityClip(NULL), _zIndexDefault(0), _focusIndexDefault(0), _focusIndexMax(0), _totalWidth(0), _totalHeight(0),
+      _scrollBarOpacityClip(NULL), _zIndexDefault(0), _focusIndexDefault(0), _focusIndexMax(0),
+      _focusPressed(0), _selectButtonDown(false),
+      _lastFrameTime(0), _focusChangeRepeat(false),
+      _focusChangeStartTime(0), _focusChangeRepeatDelay(FOCUS_CHANGE_REPEAT_DELAY), _focusChangeCount(0),
+      _totalWidth(0), _totalHeight(0),
       _contactIndices(0), _initializedWithScroll(false)
 {
 }
@@ -165,6 +176,10 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
             control = Joystick::create(controlStyle, controlSpace);
         }
+        else if (controlName == "IMAGE")
+        {
+            control = ImageControl::create(controlStyle, controlSpace);
+        }
         else
         {
             // Ignore - not a valid control name.
@@ -177,20 +192,6 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
             addControl(control);
             control->release();
-
-            if (control->getZIndex() == -1)
-            {
-                control->setZIndex(_zIndexDefault++);
-            }
-
-            if (control->getFocusIndex() == -1)
-            {
-                control->setFocusIndex(_focusIndexDefault++);
-            }
-
-            int focusIndex = control->getFocusIndex();
-            if (focusIndex > _focusIndexMax)
-                _focusIndexMax = focusIndex;
         }
 
         // Get the next control.
@@ -198,7 +199,7 @@ void Container::addControls(Theme* theme, Properties* properties)
     }
 
     // Sort controls by Z-Order.
-    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
+    sortControls();
 }
 
 Layout* Container::getLayout()
@@ -215,11 +216,26 @@ unsigned int Container::addControl(Control* control)
         control->_parent->removeControl(control);
     }
 
+    if (control->getZIndex() == -1)
+    {
+        control->setZIndex(_zIndexDefault++);
+    }
+
+    if (control->getFocusIndex() == -1)
+    {
+        control->setFocusIndex(_focusIndexDefault++);
+    }
+
+    int focusIndex = control->getFocusIndex();
+    if (focusIndex > _focusIndexMax)
+        _focusIndexMax = focusIndex;
+
     if (control->_parent != this)
     {
         _controls.push_back(control);
         control->addRef();
         control->_parent = this;
+        sortControls();
         return (unsigned int)(_controls.size() - 1);
     }
     else
@@ -433,9 +449,6 @@ void Container::update(const Control* container, const Vector2& offset)
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
 
-    // Sort controls by Z-Order.
-    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
-
     GP_ASSERT(_layout);
     if (_scroll != SCROLL_NONE)
     {
@@ -615,40 +628,524 @@ bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
             continue;
         }
 
-        if (control->getState() == Control::FOCUS)
+        if (control->getState() == Control::FOCUS && control->keyEvent(evt, key))
+        {
+            release();
+            return true;
+        }
+    }
+
+    // If we made it this far, no control handled the event.
+    if (evt == Keyboard::KEY_CHAR && key == Keyboard::KEY_TAB)
+    {
+        moveFocus(NEXT);
+        return _consumeInputEvents;
+    }
+
+    release();
+    return false;
+}
+
+void Container::guaranteeFocus(Control* inFocus)
+{
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (control == inFocus)
+            continue;
+
+        if (control->isContainer())
+        {
+            ((Container*)control)->guaranteeFocus(inFocus);
+        }
+        else if (control->getState() == Control::FOCUS)
+        {
+            control->setState(NORMAL);
+            return;
+        }
+    }
+}
+
+bool Container::moveFocus(Direction direction, Control* outsideControl)
+{
+    _direction = direction;
+
+    Control* start = outsideControl;
+    if (!start)
+    {
+        std::vector<Control*>::const_iterator it;
+        for (it = _controls.begin(); it < _controls.end(); it++)
+        {
+            Control* control = *it;
+            GP_ASSERT(control);
+            if (control->getState() == Control::FOCUS)
+            {
+                start = control;
+                break;
+            }
+        }
+    }
+
+    int focusIndex = 0;
+    Control* next = NULL;
+    if (start)
+    {
+        const Rectangle& startBounds = start->getAbsoluteBounds();
+        Vector2 vStart, vNext;
+        float distance = FLT_MAX;
+        start->setState(Control::NORMAL);
+
+        switch(direction)
+        {
+        case UP:
+            vStart.set(startBounds.x + startBounds.width * 0.5f,
+                        startBounds.y);
+            break;
+        case DOWN:
+            vStart.set(startBounds.x + startBounds.width * 0.5f,
+                        startBounds.bottom());
+            break;
+        case LEFT:
+            vStart.set(startBounds.x,
+                        startBounds.y + startBounds.height * 0.5f);
+            break;
+        case RIGHT:
+            vStart.set(startBounds.right(),
+                        startBounds.y + startBounds.height * 0.5f);
+            break;
+        case NEXT:
+            break;
+        }
+
+        if (direction != NEXT)
+        {
+            std::vector<Control*>::const_iterator itt;
+            for (itt = _controls.begin(); itt < _controls.end(); itt++)
+            {
+                Control* nextControl = *itt;
+
+                if (nextControl == start || nextControl->getFocusIndex() < 0 ||
+                    !nextControl->isEnabled() || !nextControl->isVisible())
+                {
+                    // Control is not focusable.
+                    continue;
+                }
+
+                const Rectangle& nextBounds = nextControl->getAbsoluteBounds();
+                switch(direction)
+                {
+                case UP:
+                    vNext.set(nextBounds.x + nextBounds.width * 0.5f,
+                              nextBounds.bottom());
+                    if (vNext.y > vStart.y) continue;
+                    break;
+                case DOWN:
+                    vNext.set(nextBounds.x + nextBounds.width * 0.5f,
+                              nextBounds.y);
+                    if (vNext.y < vStart.y) continue;
+                    break;
+                case LEFT:
+                    vNext.set(nextBounds.right(),
+                              nextBounds.y + nextBounds.height * 0.5f);
+                    if (vNext.x > vStart.x) continue;
+                    break;
+                case RIGHT:
+                    vNext.set(nextBounds.x,
+                              nextBounds.y + nextBounds.height * 0.5f);
+                    if (vNext.x < vStart.x) continue;
+                    break;
+                }
+
+                float nextDistance = vStart.distance(vNext);
+                if (abs(nextDistance) < distance)
+                {
+                    distance = nextDistance;
+                    next = nextControl;
+                }
+            }
+        }
+
+        if (!next)
         {
-            if (control->keyEvent(evt, key))
+            // Check for controls in the given direction in our parent container.
+            if (!outsideControl && _parent && _parent->moveFocus(direction, start))
             {
-                release();
+                setState(NORMAL);
+                _focusChangeRepeat = false;
                 return true;
             }
-            else if (evt == Keyboard::KEY_CHAR && key == Keyboard::KEY_TAB)
+            
+            // No control is in the given direction.  Move to the next control in the focus order.
+            int focusDelta;
+            switch(direction)
+            {
+            case UP:
+            case LEFT:
+                focusDelta = -1;
+                break;
+            case DOWN:
+            case RIGHT:
+            case NEXT:
+                focusDelta = 1;
+                break;
+            }
+
+            // Find the index to search for.
+            if (outsideControl)
+            {
+                focusIndex = outsideControl->_parent->getFocusIndex() + focusDelta;
+            }
+            else
+            {
+                focusIndex = start->getFocusIndex() + focusDelta;
+            }
+
+            if (focusIndex > _focusIndexMax)
             {
-                // Shift focus to next control.
-                int focusIndex = control->getFocusIndex() + 1; // Index to search for.
-                if (focusIndex > _focusIndexMax)
+                focusIndex = 0;
+            }
+            else if (focusIndex < 0)
+            {
+                focusIndex = _focusIndexMax;
+            }
+        }
+    }
+
+    if (!next)
+    {
+        std::vector<Control*>::const_iterator itt;
+        for (itt = _controls.begin(); itt < _controls.end(); itt++)
+        {
+            Control* nextControl = *itt;
+            if (nextControl->getFocusIndex() == focusIndex)
+            {
+                next = nextControl;
+            }
+        }
+    }
+
+    // If we haven't found next by now, then there are no focusable controls in this container.
+    if (next)
+    {
+        next->setState(Control::FOCUS);
+        _dirty = true;
+
+        if (next->isContainer())
+        {
+            if (((Container*)next)->moveFocus(direction, start))
+            {
+                _focusChangeRepeat = false;
+                return true;
+            }
+        }
+
+        // If the next control is not fully visible, scroll the container so that it is.
+        const Rectangle& bounds = next->getBounds();
+        if (bounds.x < _scrollPosition.x)
+        {
+            // Control is to the left of the scrolled viewport.
+            _scrollPosition.x = -bounds.x;
+        }
+        else if (bounds.x + bounds.width > _scrollPosition.x + _viewportBounds.width)
+        {
+            // Control is off to the right.
+            _scrollPosition.x = -(bounds.x + bounds.width - _viewportBounds.width);
+        }
+
+        if (bounds.y < _viewportBounds.y - _scrollPosition.y)
+        {
+            // Control is above the viewport.
+            _scrollPosition.y = -bounds.y;
+        }
+        else if (bounds.y + bounds.height > _viewportBounds.height - _scrollPosition.y)
+        {
+            // Control is below the viewport.
+            _scrollPosition.y = -(bounds.y + bounds.height - _viewportBounds.height);
+        }
+
+        if (outsideControl && outsideControl->_parent)
+        {
+            _focusPressed = outsideControl->_parent->_focusPressed;
+            _focusChangeCount = outsideControl->_parent->_focusChangeCount;
+            _focusChangeRepeatDelay = outsideControl->_parent->_focusChangeRepeatDelay;
+            outsideControl->_parent->guaranteeFocus(next);
+        }
+
+        _focusChangeStartTime = Game::getAbsoluteTime();
+        _focusChangeRepeat = true;
+        addRef();
+        Game::getInstance()->schedule(_focusChangeRepeatDelay, this);
+
+        return true;
+    }
+
+    return false;
+}
+
+void Container::timeEvent(long timeDiff, void* cookie)
+{
+    double time = Game::getAbsoluteTime();
+    if (_focusChangeRepeat && _state == FOCUS && _focusPressed &&
+        abs(time - timeDiff - _focusChangeRepeatDelay - _focusChangeStartTime) < 50)
+    {
+        ++_focusChangeCount;
+        if (_focusChangeCount == 5)
+        {
+            _focusChangeRepeatDelay *= 0.5;
+        }
+        moveFocus(_direction);
+    }
+    else
+    {
+        _focusChangeCount = 0;
+        _focusChangeRepeatDelay = FOCUS_CHANGE_REPEAT_DELAY;
+    }
+
+    release();
+}
+
+void Container::startScrolling(float x, float y, bool resetTime)
+{
+    _scrollingVelocity.set(-x, y);
+    _scrolling = true;
+    _scrollBarOpacity = 1.0f;
+    _dirty = true;
+
+    if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
+    {
+        _scrollBarOpacityClip->stop();
+        _scrollBarOpacityClip = NULL;
+    }
+
+    if (resetTime)
+    {
+        _lastFrameTime = Game::getGameTime();
+    }
+}
+
+void Container::stopScrolling()
+{
+    _scrollingVelocity.set(0, 0);
+    _scrolling = false;
+    _dirty = true;
+}
+
+bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    addRef();
+
+    bool eventConsumed = false;
+
+    // Pass the event to any control that is active or in focus.
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (control->getState() == Control::FOCUS || control->getState() == Control::ACTIVE)
+        {
+            eventConsumed |= control->gamepadEvent(evt, gamepad, analogIndex);
+            break;
+        }
+    }
+
+    // First check if a selection button is down.
+    if (!_selectButtonDown)
+    {
+        if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+            gamepad->isButtonDown(Gamepad::BUTTON_X))
+        {
+            _selectButtonDown = true;
+            _focusChangeRepeat = false;
+            eventConsumed |= _consumeInputEvents;
+        }
+    }
+    else
+    {
+        if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_X))
+        {
+            _selectButtonDown = false;
+        }
+    }
+
+    Vector2 joystick;
+    gamepad->getJoystickValues(analogIndex, &joystick);
+
+    // Don't allow focus changes or scrolling while a selection button is down.
+    if (!_selectButtonDown && !eventConsumed)
+    {
+        switch (evt)
+        {
+            case Gamepad::BUTTON_EVENT:
+            {
+                // Shift focus forward or backward when the DPad is used.
+                if (!(_focusPressed & DOWN) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_DOWN))
                 {
-                    focusIndex = 0;
+                    _focusPressed |= DOWN;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(DOWN))
+                        break;
+                    else
+                        startScrolling(0, -GAMEPAD_SCROLL_SPEED);
+                }
+                    
+                if (!(_focusPressed & RIGHT) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_RIGHT))
+                {
+                    _focusPressed |= RIGHT;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(RIGHT))
+                        break;
+                    else
+                        startScrolling(GAMEPAD_SCROLL_SPEED, 0);
                 }
-                control->setState(Control::NORMAL);
 
-                std::vector<Control*>::const_iterator itt;
-                for (itt = _controls.begin(); itt < _controls.end(); itt++)
+                if (!(_focusPressed & UP) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_UP))
                 {
-                    Control* nextControl = *itt;
-                    if (nextControl->getFocusIndex() == focusIndex)
+                    _focusPressed |= UP;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(UP))
+                        break;
+                    else
+                        startScrolling(0, GAMEPAD_SCROLL_SPEED);
+                }
+
+                if (!(_focusPressed & LEFT) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_LEFT))
+                {
+                    _focusPressed |= LEFT;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(LEFT))
+                        break;
+                    else
+                        startScrolling(-GAMEPAD_SCROLL_SPEED, 0);
+                }
+                break;
+            }
+            case Gamepad::JOYSTICK_EVENT:
+            {
+                switch (analogIndex)
+                {
+                case 0:
+                    // The left analog stick can be used in the same way as the DPad.
+                    eventConsumed |= _consumeInputEvents;
+                    if (!(_focusPressed & RIGHT) &&
+                        joystick.x > JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= RIGHT;
+                        if (moveFocus(RIGHT))
+                            break;
+                        else
+                            startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, 0);
+                    }
+
+                    if (!(_focusPressed & DOWN) &&
+                        joystick.y < -JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= DOWN;
+                        if (moveFocus(DOWN))
+                            break;
+                        else
+                            startScrolling(0, GAMEPAD_SCROLL_SPEED * joystick.y);
+                    }
+
+                    if (!(_focusPressed & LEFT) &&
+                        joystick.x < -JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= LEFT;
+                        if (moveFocus(LEFT))
+                            break;
+                        else
+                            startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, 0);
+                    }
+                        
+                    if (!(_focusPressed & UP) &&
+                        joystick.y > JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= UP;
+                        if (moveFocus(UP))
+                            break;
+                        else
+                            startScrolling(0, GAMEPAD_SCROLL_SPEED * joystick.y);
+                    }
+                    break;
+
+                case 1:
+                    // The right analog stick can be used to scroll.
+                    if (_scroll != SCROLL_NONE)
                     {
-                        nextControl->setState(Control::FOCUS);
+                        if (_scrolling)
+                        {
+                            if (joystick.isZero())
+                            {
+                                stopScrolling();
+                            }
+                            else
+                            {
+                                startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, GAMEPAD_SCROLL_SPEED * joystick.y, false);
+                            }
+                        }
+                        else
+                        {
+                            startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, GAMEPAD_SCROLL_SPEED * joystick.y);
+                        }
                         release();
                         return _consumeInputEvents;
                     }
+                    break;
                 }
             }
         }
     }
 
+    if ((evt == Gamepad::BUTTON_EVENT || evt == Gamepad::JOYSTICK_EVENT) &&
+        analogIndex == 0)
+    {
+        if ((_focusPressed & DOWN) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_DOWN) &&
+            joystick.y > -JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~DOWN;
+            eventConsumed |= _consumeInputEvents;
+        }
+
+        if ((_focusPressed & RIGHT) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_RIGHT) &&
+            joystick.x < JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~RIGHT;
+            eventConsumed |= _consumeInputEvents;
+        }
+    
+        if ((_focusPressed & UP) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_UP) &&
+            joystick.y < JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~UP;
+            eventConsumed |= _consumeInputEvents;
+        }
+
+        if ((_focusPressed & LEFT) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_LEFT) &&
+            joystick.x > -JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~LEFT;
+            eventConsumed |= _consumeInputEvents;
+        }
+    }
+
+    if (!_focusPressed && _scrolling)
+    {
+        stopScrolling();
+    }
+
     release();
-    return false;
+    return eventConsumed;
 }
 
 bool Container::isContainer() const
@@ -692,11 +1189,14 @@ void Container::updateScroll()
         _layout->update(this, _scrollPosition);
     }
 
-    // Update Time.
-    static double lastFrameTime = Game::getGameTime();
+    // Update time.
+    if (!_lastFrameTime)
+    {
+        _lastFrameTime = Game::getGameTime();
+    }
     double frameTime = Game::getGameTime();
-    float elapsedTime = (float)(frameTime - lastFrameTime);
-    lastFrameTime = frameTime;
+    float elapsedTime = (float)(frameTime - _lastFrameTime);
+    _lastFrameTime = frameTime;
 
     const Theme::Border& containerBorder = getBorder(_state);
     const Theme::Padding& containerPadding = getPadding();
@@ -729,7 +1229,7 @@ void Container::updateScroll()
     float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
 
     // Apply and dampen inertia.
-    if (!_scrolling && !_scrollingVelocity.isZero())
+    if (!_scrollingVelocity.isZero())
     {
         // Calculate the time passed since last update.
         float elapsedSecs = (float)elapsedTime * 0.001f;
@@ -737,14 +1237,17 @@ void Container::updateScroll()
         _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
         _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
 
-        float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
-        _scrollingVelocity.x *= dampening;
-        _scrollingVelocity.y *= dampening;
-
-        if (fabs(_scrollingVelocity.x) < 100.0f)
-            _scrollingVelocity.x = 0.0f;
-        if (fabs(_scrollingVelocity.y) < 100.0f)
-            _scrollingVelocity.y = 0.0f;
+        if (!_scrolling)
+        {
+            float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
+            _scrollingVelocity.x *= dampening;
+            _scrollingVelocity.y *= dampening;
+
+            if (fabs(_scrollingVelocity.x) < 100.0f)
+                _scrollingVelocity.x = 0.0f;
+            if (fabs(_scrollingVelocity.y) < 100.0f)
+                _scrollingVelocity.y = 0.0f;
+        }
     }
 
     // Stop scrolling when the far edge is reached.
@@ -791,7 +1294,7 @@ void Container::updateScroll()
         _scrollBarOpacity = 0.99f;
         if (!_scrollBarOpacityClip)
         {
-            Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
+            Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_SCROLLBAR_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
             _scrollBarOpacityClip = animation->getClip();
         }
         _scrollBarOpacityClip->play();
@@ -801,6 +1304,14 @@ void Container::updateScroll()
     _layout->update(this, _scrollPosition);
 }
 
+void Container::sortControls()
+{
+    if (_layout->getType() == Layout::LAYOUT_ABSOLUTE)
+    {
+        std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
+    }
+}
+
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     switch(evt)
@@ -1180,7 +1691,7 @@ unsigned int Container::getAnimationPropertyComponentCount(int propertyId) const
 {
     switch(propertyId)
     {
-    case ANIMATE_OPACITY:
+    case ANIMATE_SCROLLBAR_OPACITY:
         return 1;
     default:
         return Control::getAnimationPropertyComponentCount(propertyId);
@@ -1193,7 +1704,7 @@ void Container::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 
     switch(propertyId)
     {
-    case ANIMATE_OPACITY:
+    case ANIMATE_SCROLLBAR_OPACITY:
         value->setFloat(0, _scrollBarOpacity);
         break;
     default:
@@ -1208,7 +1719,7 @@ void Container::setAnimationPropertyValue(int propertyId, AnimationValue* value,
 
     switch(propertyId)
     {
-    case ANIMATE_OPACITY:
+    case ANIMATE_SCROLLBAR_OPACITY:
         _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
         _dirty = true;
         break;

+ 54 - 4
gameplay/src/Container.h

@@ -3,6 +3,7 @@
 
 #include "Control.h"
 #include "Layout.h"
+#include "TimeListener.h"
 
 namespace gameplay
 {
@@ -44,7 +45,7 @@ namespace gameplay
     }
  @endverbatim
  */
-class Container : public Control
+class Container : public Control, TimeListener
 {
 
 public:
@@ -212,6 +213,13 @@ public:
      */
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
+    /**
+     * @see TimeListener::timeEvent
+     *
+     * @script{ignore}
+     */
+    void timeEvent(long timeDiff, void* cookie);
+
 protected:
 
     /**
@@ -291,10 +299,17 @@ protected:
      *
      * @return True if the mouse event is consumed or false if it is not consumed.
      *
-     * @see Mouse::MouseEvent
+     * @see Mouse::mouseEvent
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Gets a Layout::Type enum from a matching string.
      *
@@ -334,6 +349,13 @@ protected:
      */
     void updateScroll();
 
+    /**
+     * Sorts controls by Z-Order (for absolute layouts only).
+     * This method is used by controls to notify their parent container when
+     * their Z-Index changes.
+     */
+    void sortControls();
+
     /**
      * Applies touch events to scroll state.
      *
@@ -513,16 +535,44 @@ private:
      */
     Container(const Container& copy);
 
+    enum Direction
+    {
+        UP = 0x01,
+        DOWN = 0x02,
+        LEFT = 0x04,
+        RIGHT = 0x08,
+        NEXT = 0x10
+    };
+
+    // Returns true on success; false if there are no controls to focus on,
+    // in which case scrolling can be initiated.
+    bool moveFocus(Direction direction, Control* outsideControl = NULL);
+
+    void guaranteeFocus(Control* inFocus);
+
+    // Starts scrolling at the given horizontal and vertical speeds.
+    void startScrolling(float x, float y, bool resetTime = true);
+
+    void stopScrolling();
+
     AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
     int _focusIndexDefault;
     int _focusIndexMax;
+    unsigned int _focusPressed;
+    bool _selectButtonDown;
+    double _lastFrameTime;
+
+    // Timing information for repeating focus changes.
+    bool _focusChangeRepeat;
+    double _focusChangeStartTime;
+    double _focusChangeRepeatDelay;
+    unsigned int _focusChangeCount;
+    Direction _direction;
 
     float _totalWidth;
     float _totalHeight;
-
     int _contactIndices;
-
     bool _initializedWithScroll;
 };
 

+ 77 - 35
gameplay/src/Control.cpp

@@ -8,7 +8,7 @@ namespace gameplay
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
     _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
-    _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(0), _parent(NULL), _styleOverridden(false), _skin(NULL)
+    _zIndex(-1), _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _parent(NULL), _styleOverridden(false), _skin(NULL)
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
 }
@@ -17,9 +17,9 @@ Control::~Control()
 {
     if (_listeners)
     {
-        for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
+        for (std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
         {
-            std::list<Listener*>* list = itr->second;
+            std::list<Control::Listener*>* list = itr->second;
             SAFE_DELETE(list);
         }
         SAFE_DELETE(_listeners);
@@ -187,6 +187,11 @@ const Rectangle& Control::getBounds() const
     return _bounds;
 }
 
+const Rectangle& Control::getAbsoluteBounds() const
+{
+    return _absoluteBounds;
+}
+
 float Control::getX() const
 {
     return _bounds.x;
@@ -691,29 +696,29 @@ void Control::addListener(Control::Listener* listener, int eventFlags)
 {
     GP_ASSERT(listener);
 
-    if ((eventFlags & Listener::PRESS) == Listener::PRESS)
+    if ((eventFlags & Control::Listener::PRESS) == Control::Listener::PRESS)
     {
-        addSpecificListener(listener, Listener::PRESS);
+        addSpecificListener(listener, Control::Listener::PRESS);
     }
 
-    if ((eventFlags & Listener::RELEASE) == Listener::RELEASE)
+    if ((eventFlags & Control::Listener::RELEASE) == Control::Listener::RELEASE)
     {
-        addSpecificListener(listener, Listener::RELEASE);
+        addSpecificListener(listener, Control::Listener::RELEASE);
     }
 
-    if ((eventFlags & Listener::CLICK) == Listener::CLICK)
+    if ((eventFlags & Control::Listener::CLICK) == Control::Listener::CLICK)
     {
-        addSpecificListener(listener, Listener::CLICK);
+        addSpecificListener(listener, Control::Listener::CLICK);
     }
 
-    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
+    if ((eventFlags & Control::Listener::VALUE_CHANGED) == Control::Listener::VALUE_CHANGED)
     {
-        addSpecificListener(listener, Listener::VALUE_CHANGED);
+        addSpecificListener(listener, Control::Listener::VALUE_CHANGED);
     }
 
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
-        addSpecificListener(listener, Listener::TEXT_CHANGED);
+        addSpecificListener(listener, Control::Listener::TEXT_CHANGED);
     }
 }
 
@@ -722,13 +727,13 @@ void Control::removeListener(Control::Listener* listener)
     if (_listeners == NULL || listener == NULL)
         return;
 
-    for (std::map<Listener::EventType, std::list<Listener*>*>::iterator itr = _listeners->begin(); itr != _listeners->end();)
+    for (std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::iterator itr = _listeners->begin(); itr != _listeners->end();)
     {
         itr->second->remove(listener);
 
         if(itr->second->empty())
         {
-            std::list<Listener*>* list = itr->second;
+            std::list<Control::Listener*>* list = itr->second;
             _listeners->erase(itr++);
             SAFE_DELETE(list);
         }
@@ -740,23 +745,23 @@ void Control::removeListener(Control::Listener* listener)
         SAFE_DELETE(_listeners);
 }
 
-void Control::addSpecificListener(Control::Listener* listener, Listener::EventType eventType)
+void Control::addSpecificListener(Control::Listener* listener, Control::Listener::EventType eventType)
 {
     GP_ASSERT(listener);
 
     if (!_listeners)
     {
-        _listeners = new std::map<Listener::EventType, std::list<Listener*>*>();
+        _listeners = new std::map<Control::Listener::EventType, std::list<Control::Listener*>*>();
     }
 
-    std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
+    std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->find(eventType);
     if (itr == _listeners->end())
     {
-        _listeners->insert(std::make_pair(eventType, new std::list<Listener*>()));
+        _listeners->insert(std::make_pair(eventType, new std::list<Control::Listener*>()));
         itr = _listeners->find(eventType);
     }
 
-    std::list<Listener*>* listenerList = itr->second;
+    std::list<Control::Listener*>* listenerList = itr->second;
     listenerList->push_back(listener);
 }
 
@@ -773,7 +778,7 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
             _contactIndex = (int) contactIndex;
 
-            notifyListeners(Listener::PRESS);
+            notifyListeners(Control::Listener::PRESS);
 
             return _consumeInputEvents;
         }
@@ -789,15 +794,15 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
             _contactIndex = INVALID_CONTACT_INDEX;
 
-            // Always trigger Listener::RELEASE
-            notifyListeners(Listener::RELEASE);
+            // Always trigger Control::Listener::RELEASE
+            notifyListeners(Control::Listener::RELEASE);
 
-            // Only trigger Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
+            // Only trigger Control::Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
             if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
                 // Leave this control in the FOCUS state.
-                notifyListeners(Listener::CLICK);
+                notifyListeners(Control::Listener::CLICK);
             }
 
             return _consumeInputEvents;
@@ -834,7 +839,42 @@ bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
     return false;
 }
 
-void Control::notifyListeners(Listener::EventType eventType)
+bool Control::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    // Default behavior for gamepad events.
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::FOCUS)
+        {
+            if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+                gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::PRESS);
+                return _consumeInputEvents;
+            }
+        }
+        else if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::RELEASE);
+                notifyListeners(Control::Listener::CLICK);
+                return _consumeInputEvents;
+            }
+        }
+        break;
+    case Gamepad::JOYSTICK_EVENT:
+        break;
+    case Gamepad::TRIGGER_EVENT:
+        break;
+    }
+
+    return false;
+}
+
+void Control::notifyListeners(Control::Listener::EventType eventType)
 {
     // This method runs untrusted code by notifying listeners of events.
     // If the user calls exit() or otherwise releases this control, we
@@ -843,11 +883,11 @@ void Control::notifyListeners(Listener::EventType eventType)
 
     if (_listeners)
     {
-        std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
+        std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->find(eventType);
         if (itr != _listeners->end())
         {
-            std::list<Listener*>* listenerList = itr->second;
-            for (std::list<Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); ++listenerItr)
+            std::list<Control::Listener*>* listenerList = itr->second;
+            for (std::list<Control::Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); ++listenerItr)
             {
                 GP_ASSERT(*listenerItr);
                 (*listenerItr)->controlEvent(this, eventType);
@@ -951,9 +991,9 @@ void Control::update(const Control* container, const Vector2& offset)
  
     _viewportClipBounds.set(x, y, width, height);
 
-    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
-        width + border.left + padding.left + border.right + padding.right,
-        height + border.top + padding.top + border.bottom + padding.bottom);
+    width += border.left + padding.left + border.right + padding.right;
+    height += border.top + padding.top + border.bottom + padding.bottom;
+    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top, max(width, 0.0f), max(height, 0.0f));
     if (_clearBounds.isEmpty())
     {
         _clearBounds.set(_absoluteClipBounds);
@@ -1011,9 +1051,11 @@ void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
             spriteBatch->draw(rightX, _absoluteBounds.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
         if (border.left)
             spriteBatch->draw(_absoluteBounds.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
-        if (border.left && border.right && border.top && border.bottom)
-            spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
-                center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+
+        // Always draw the background.
+        spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
+            center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+
         if (border.right)
             spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
         if (border.bottom && border.left)

+ 37 - 17
gameplay/src/Control.h

@@ -11,6 +11,7 @@
 #include "Keyboard.h"
 #include "Mouse.h"
 #include "ScriptTarget.h"
+#include "Gamepad.h"
 
 namespace gameplay
 {
@@ -85,12 +86,6 @@ public:
         ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT
     };
 
-    /**
-     * @script{ignore}
-     * A constant used for setting themed attributes on all control states simultaneously.
-     */
-    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
-
     /**
      * Implement Control::Listener and call Control::addListener()
      * in order to listen for events on controls.
@@ -156,6 +151,12 @@ public:
         virtual void controlEvent(Control* control, EventType evt) = 0;
     };
 
+    /**
+     * @script{ignore}
+     * A constant used for setting themed attributes on all control states simultaneously.
+     */
+    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
+
     /**
      * Position animation property. Data = x, y
      */
@@ -244,6 +245,14 @@ public:
      */
     const Rectangle& getBounds() const;
 
+    /**
+     * Get the absolute bounds of this control, in pixels, including border and padding,
+     * before clipping.
+     *
+     * @return The absolute bounds of this control.
+     */
+    const Rectangle& getAbsoluteBounds() const;
+
     /**
      * Get the x coordinate of this control's bounds.
      *
@@ -841,6 +850,15 @@ protected:
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @param gamepad The gamepad whose state changed.
+     * @param evt The gamepad event that occurred.
+     * @param analogIndex If evt is JOYSTICK_EVENT or TRIGGER_EVENT, this will be the index of the corresponding control.
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
@@ -850,6 +868,14 @@ protected:
      */
     virtual void update(const Control* container, const Vector2& offset);
 
+    /**
+     * Draws the themed border and background of a control.
+     *
+     * @param spriteBatch The sprite batch containing this control's border images.
+     * @param clip The clipping rectangle of this control's parent container.
+     */
+    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+
     /**
      * Draw the images associated with this control.
      *
@@ -915,7 +941,8 @@ protected:
      *
      * @param eventType The event to trigger.
      */
-    void notifyListeners(Listener::EventType eventType);
+    //void notifyListeners(Listener::EventType eventType);
+    void notifyListeners(Control::Listener::EventType eventType);
 
     /**
      * Gets the Alignment by string.
@@ -1003,7 +1030,8 @@ protected:
     /**
      * Listeners map of EventType's to a list of Listeners.
      */
-    std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
+    //std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
+    std::map<Control::Listener::EventType, std::list<Control::Listener*>*>* _listeners;
     
     /**
      * The Control's Theme::Style.
@@ -1063,15 +1091,7 @@ private:
 
     Theme::Skin* getSkin(State state);
 
-    void addSpecificListener(Control::Listener* listener, Listener::EventType eventType);
-    
-    /**
-     * Draws the themed border and background of a control.
-     *
-     * @param spriteBatch The sprite batch containing this control's border images.
-     * @param clip The clipping rectangle of this control's parent container.
-     */
-    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void addSpecificListener(Control::Listener* listener, Control::Listener::EventType eventType);
     
     bool _styleOverridden;
     Theme::Skin* _skin;

+ 79 - 18
gameplay/src/Curve.cpp

@@ -178,30 +178,93 @@ void Curve::setTangent(unsigned int index, InterpolationType type, float* inValu
 
 void Curve::evaluate(float time, float* dst) const
 {
-    assert(dst && time >= 0 && time <= 1.0f);
+    assert(dst);
 
-    // Check if the point count is 1.
-    // Check if we are at or beyond the bounds of the curve.
-    if (_pointCount == 1 || time <= _points[0].time)
+    evaluate(time, 0.0f, 1.0f, 0.0f, dst);
+}
+
+void Curve::evaluate(float time, float startTime, float endTime, float loopBlendTime, float* dst) const
+{
+    assert(dst && startTime >= 0.0f && startTime <= endTime && endTime <= 1.0f && loopBlendTime >= 0.0f);
+
+    // If there's only one point on the curve, return its value.
+    if (_pointCount == 1)
     {
         memcpy(dst, _points[0].value, _componentSize);
         return;
     }
-    else if (time >= _points[_pointCount - 1].time)
+
+    unsigned int min = 0;
+    unsigned int max = _pointCount - 1;
+    float localTime = time;
+    if (startTime > 0.0f || endTime < 1.0f)
+    {
+        // Evaluating a sub section of the curve
+        min = determineIndex(startTime, 0, max);
+        max = determineIndex(endTime, min, max);
+
+        // Convert time to fall within the subregion
+        localTime = _points[min].time + (_points[max].time - _points[min].time) * time;
+    }
+
+    if (loopBlendTime == 0.0f)
+    {
+        // If no loop blend time is specified, clamp time to end points
+        if (localTime < _points[min].time)
+            localTime = _points[min].time;
+        else if (localTime > _points[max].time)
+            localTime = _points[max].time;
+    }
+
+    // If an exact endpoint was specified, skip interpolation and return the value directly
+    if (localTime == _points[min].time)
+    {
+        memcpy(dst, _points[min].value, _componentSize);
+        return;
+    }
+    if (localTime == _points[max].time)
     {
-        memcpy(dst, _points[_pointCount - 1].value, _componentSize);
+        memcpy(dst, _points[max].value, _componentSize);
         return;
     }
 
-    // Locate the points we are interpolating between using a binary search.
-    unsigned int index = determineIndex(time);
-    
-    Point* from = _points + index;
-    Point* to = _points + (index + 1);
+    Point* from;
+    Point* to;
+    float scale;
+    float t;
+    unsigned int index;
+
+    if (localTime > _points[max].time)
+    {
+        // Looping forward
+        index = max;
+        from = &_points[max];
+        to = &_points[min];
 
-    // Calculate the fractional time between the two points.
-    float scale = (to->time - from->time);
-    float t = (time - from->time) / scale;
+        // Calculate the fractional time between the two points.
+        t = (localTime - from->time) / loopBlendTime;
+    }
+    else if (localTime < _points[min].time)
+    {
+        // Looping in reverse
+        index = min;
+        from = &_points[min];
+        to = &_points[max];
+
+        // Calculate the fractional time between the two points.
+        t = (from->time - localTime) / loopBlendTime;
+    }
+    else
+    {
+        // Locate the points we are interpolating between using a binary search.
+        index = determineIndex(localTime, min, max);
+        from = &_points[index];
+        to = &_points[index == max ? index : index+1];
+
+        // Calculate the fractional time between the two points.
+        scale = (to->time - from->time);
+        t = (localTime - from->time) / scale;
+    }
 
     // Calculate the value of the curve discretely if appropriate.
     switch (from->type)
@@ -1157,11 +1220,9 @@ void Curve::interpolateQuaternion(float s, float* from, float* to, float* dst) c
         Quaternion::slerp(to[0], to[1], to[2], to[3], from[0], from[1], from[2], from[3], s, dst, dst + 1, dst + 2, dst + 3);
 }
 
-int Curve::determineIndex(float time) const
+int Curve::determineIndex(float time, unsigned int min, unsigned int max) const
 {
-    unsigned int min = 0;
-    unsigned int max = _pointCount - 1;
-    unsigned int mid = 0;
+    unsigned int mid;
 
     // Do a binary search to determine the index.
     do 

+ 34 - 2
gameplay/src/Curve.h

@@ -345,13 +345,45 @@ public:
     void setTangent(unsigned int index, InterpolationType type, float* inValue, float* outValue);
     
     /**
-     * Evaluates the curve at the given position value (between 0.0 and 1.0 inclusive).
+     * Evaluates the curve at the given position value.
+     *
+     * Time should generally be specified as a value between 0.0 - 1.0, inclusive.
+     * A value outside this range can also be specified to perform an interpolation
+     * between the two end points of the curve. This can be useful for smoothly
+     * interpolating a repeat of the curve.
      *
      * @param time The position to evaluate the curve at.
      * @param dst The evaluated value of the curve at the given time.
      */
     void evaluate(float time, float* dst) const;
 
+    /**
+     * Evaluates the curve at the given position value (between 0.0 and 1.0 inclusive)
+     * within the specified subregion of the curve.
+     *
+     * This method is useful for evaluating sub sections of the curve. A common use for
+     * this is when evaluating individual animation clips that are positioned within a
+     * larger animation curve. This method also allows looping to occur between the
+     * end points of curve sub regions, with optional blending/interpolation between
+     * the end points (using the loopBlendTime parameter).
+     *
+     * Time should generally be specified as a value between 0.0 - 1.0, inclusive.
+     * A value outside this range can also be specified to perform an interpolation
+     * between the two end points of the curve. This can be useful for smoothly
+     * interpolating a repeat of the curve.
+     *
+     * @param time The position within the subregion of the curve to evaluate the curve at.
+     *      A time of zero representes the start of the subregion, with a time of one
+     *      representing the end of the subregion.
+     * @param startTime Start time for the subregion (between 0.0 - 1.0).
+     * @param endTime End time for the subregion (between 0.0 - 1.0).
+     * @param loopBlendTime Time (in milliseconds) to blend between the end points of the curve
+     *      for looping purposes when time is outside the range 0-1. A value of zero here
+     *      disables curve looping.
+     * @param dst The evaluated value of the curve at the given time.
+     */
+    void evaluate(float time, float startTime, float endTime, float loopBlendTime, float* dst) const;
+
     /**
      * Linear interpolation function.
      */
@@ -459,7 +491,7 @@ private:
     /**
      * Determines the current keyframe to interpolate from based on the specified time.
      */ 
-    int determineIndex(float time) const;
+    int determineIndex(float time, unsigned int min, unsigned int max) const;
 
     /**
      * Sets the offset for the beginning of a Quaternion piece of data within the curve's value span at the specified

+ 70 - 26
gameplay/src/FileSystem.cpp

@@ -86,6 +86,27 @@ static bool androidFileExists(const char* filePath)
 static std::string __resourcePath("./");
 static std::map<std::string, std::string> __aliases;
 
+/**
+ * Gets the fully resolved path.
+ * If the path is relative then it will be prefixed with the resource path.
+ * Aliases will be converted to a relative path.
+ * 
+ * @param path The path to resolve.
+ * @param fullPath The full resolved path. (out param)
+ */
+static void getFullPath(const char* path, std::string& fullPath)
+{
+    if (FileSystem::isAbsolutePath(path))
+    {
+        fullPath.assign(path);
+    }
+    else
+    {
+        fullPath.assign(__resourcePath);
+        fullPath += FileSystem::resolvePath(path);
+    }
+}
+
 /**
  * 
  * @script{ignore}
@@ -259,30 +280,54 @@ bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
         path.append(dirPath);
     }
     path.append("/.");
+    bool result = false;
+
     struct dirent* dp;
     DIR* dir = opendir(path.c_str());
-    if (!dir)
+    if (dir != NULL)
     {
-        return false;
+        while ((dp = readdir(dir)) != NULL)
+        {
+            std::string filepath(path);
+            filepath.append("/");
+            filepath.append(dp->d_name);
+
+            struct stat buf;
+            if (!stat(filepath.c_str(), &buf))
+            {
+                // Add to the list if this is not a directory
+                if (!S_ISDIR(buf.st_mode))
+                {
+                    files.push_back(dp->d_name);
+                }
+            }
+        }
+        closedir(dir);
+        result = true;
     }
-    while ((dp = readdir(dir)) != NULL)
-    {
-        std::string filepath(path);
-        filepath.append("/");
-        filepath.append(dp->d_name);
 
-        struct stat buf;
-        if (!stat(filepath.c_str(), &buf))
+#ifdef __ANDROID__
+    // List the files that are in the android APK at this path
+    AAssetDir* assetDir = AAssetManager_openDir(__assetManager, dirPath);
+    if (assetDir != NULL)
+    {
+        AAssetDir_rewind(assetDir);
+        const char* file = NULL;
+        while ((file = AAssetDir_getNextFileName(assetDir)) != NULL)
         {
-            // Add to the list if this is not a directory
-            if (!S_ISDIR(buf.st_mode))
+            std::string filename(file);
+            // Check if this file was already added to the list because it was copied to the SD card.
+            if (find(files.begin(), files.end(), filename) == files.end())
             {
-                files.push_back(dp->d_name);
+                files.push_back(filename);
             }
         }
+        AAssetDir_close(assetDir);
+        result = true;
     }
-    closedir(dir);
-    return true;
+#endif
+
+    return result;
 #endif
 }
 
@@ -297,13 +342,13 @@ bool FileSystem::fileExists(const char* filePath)
     }
 #endif
 
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(filePath);
+    std::string fullPath;
+    getFullPath(filePath, fullPath);
 
     gp_stat_struct s;
 
 #ifdef WIN32
-    if (stat(fullPath.c_str(), &s) != 0)
+    if (!isAbsolutePath(filePath) && stat(fullPath.c_str(), &s) != 0)
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
@@ -352,12 +397,12 @@ Stream* FileSystem::open(const char* path, size_t mode)
         return FileStreamAndroid::create(resolvePath(path), modeStr);
     }
 #else
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(path);
+    std::string fullPath;
+    getFullPath(path, fullPath);
     
 #ifdef WIN32
     gp_stat_struct s;
-    if (stat(fullPath.c_str(), &s) != 0 && (mode & WRITE) == 0)
+    if (!isAbsolutePath(path) && stat(fullPath.c_str(), &s) != 0 && (mode & WRITE) == 0)
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
@@ -386,15 +431,15 @@ FILE* FileSystem::openFile(const char* filePath, const char* mode)
     GP_ASSERT(filePath);
     GP_ASSERT(mode);
 
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(filePath);
+    std::string fullPath;
+    getFullPath(filePath, fullPath);
 
     createFileFromAsset(filePath);
     
     FILE* fp = fopen(fullPath.c_str(), mode);
     
 #ifdef WIN32
-    if (fp == NULL)
+    if (fp == NULL && !isAbsolutePath(filePath))
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
@@ -452,11 +497,10 @@ bool FileSystem::isAbsolutePath(const char* filePath)
     if (filePath == 0 || filePath[0] == '\0')
         return false;
 #ifdef WIN32
-    if (strlen(filePath) >= 2)
+    if (filePath[1] != '\0')
     {
         char first = filePath[0];
-        if (filePath[1] == ':' && ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')))
-            return true;
+        return (filePath[1] == ':' && ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')));
     }
     return false;
 #else

+ 163 - 127
gameplay/src/Form.cpp

@@ -389,138 +389,138 @@ void Form::setNode(Node* node)
 }
 
 void Form::update(float elapsedTime)
-{
-    updateBounds();
-}
-
-void Form::updateBounds()
 {
     if (isDirty())
     {
-        _clearBounds.set(_absoluteClipBounds);
-
-        // Calculate the clipped bounds.
-        float x = 0;
-        float y = 0;
-        float width = _bounds.width;
-        float height = _bounds.height;
-
-        Rectangle clip(0, 0, _bounds.width, _bounds.height);
+        updateBounds();
 
-        float clipX2 = clip.x + clip.width;
-        float x2 = clip.x + x + width;
-        if (x2 > clipX2)
-            width -= x2 - clipX2;
-
-        float clipY2 = clip.y + clip.height;
-        float y2 = clip.y + y + height;
-        if (y2 > clipY2)
-            height -= y2 - clipY2;
+        // Cache themed attributes for performance.
+        _skin = getSkin(_state);
+        _opacity = getOpacity(_state);
 
-        if (x < 0)
+        GP_ASSERT(_layout);
+        if (_scroll != SCROLL_NONE)
         {
-            width += x;
-            x = -x;
+            updateScroll();
         }
         else
         {
-            x = 0;
+            _layout->update(this, Vector2::zero());
         }
+    }
+}
 
-        if (y < 0)
-        {
-            height += y;
-            y = -y;
-        }
-        else
-        {
-            y = 0;
-        }
+void Form::updateBounds()
+{   
+    _clearBounds.set(_absoluteClipBounds);
+
+    // Calculate the clipped bounds.
+    float x = 0;
+    float y = 0;
+    float width = _bounds.width;
+    float height = _bounds.height;
 
-        _clipBounds.set(x, y, width, height);
+    Rectangle clip(0, 0, _bounds.width, _bounds.height);
 
-        // Calculate the absolute bounds.
+    float clipX2 = clip.x + clip.width;
+    float x2 = clip.x + x + width;
+    if (x2 > clipX2)
+        width -= x2 - clipX2;
+
+    float clipY2 = clip.y + clip.height;
+    float y2 = clip.y + y + height;
+    if (y2 > clipY2)
+        height -= y2 - clipY2;
+
+    if (x < 0)
+    {
+        width += x;
+        x = -x;
+    }
+    else
+    {
         x = 0;
+    }
+
+    if (y < 0)
+    {
+        height += y;
+        y = -y;
+    }
+    else
+    {
         y = 0;
-        _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
+    }
 
-        // Calculate the absolute viewport bounds. Absolute bounds minus border and padding.
-        const Theme::Border& border = getBorder(_state);
-        const Theme::Padding& padding = getPadding();
+    _clipBounds.set(x, y, width, height);
 
-        x += border.left + padding.left;
-        y += border.top + padding.top;
-        width = _bounds.width - border.left - padding.left - border.right - padding.right;
-        height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
+    // Calculate the absolute bounds.
+    x = 0;
+    y = 0;
+    _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
 
-        _viewportBounds.set(x, y, width, height);
+    // Calculate the absolute viewport bounds. Absolute bounds minus border and padding.
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
 
-        // Calculate the clip area. Absolute bounds, minus border and padding, clipped to the parent container's clip area.
-        clipX2 = clip.x + clip.width;
-        x2 = x + width;
-        if (x2 > clipX2)
-            width = clipX2 - x;
+    x += border.left + padding.left;
+    y += border.top + padding.top;
+    width = _bounds.width - border.left - padding.left - border.right - padding.right;
+    height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
 
-        clipY2 = clip.y + clip.height;
-        y2 = y + height;
-        if (y2 > clipY2)
-            height = clipY2 - y;
+    _viewportBounds.set(x, y, width, height);
 
-        if (x < clip.x)
-        {
-            float dx = clip.x - x;
-            width -= dx;
-            x = clip.x;
-        }
+    // Calculate the clip area. Absolute bounds, minus border and padding, clipped to the parent container's clip area.
+    clipX2 = clip.x + clip.width;
+    x2 = x + width;
+    if (x2 > clipX2)
+        width = clipX2 - x;
 
-        if (y < clip.y)
-        {
-            float dy = clip.y - y;
-            height -= dy;
-            y = clip.y;
-        }
- 
-        _viewportClipBounds.set(x, y, width, height);
-        _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
-                                width + border.left + padding.left + border.right + padding.right,
-                                height + border.top + padding.top + border.bottom + padding.bottom);
-        if (_clearBounds.isEmpty())
-        {
-            _clearBounds.set(_absoluteClipBounds);
-        }
+    clipY2 = clip.y + clip.height;
+    y2 = y + height;
+    if (y2 > clipY2)
+        height = clipY2 - y;
 
-        // Cache themed attributes for performance.
-        _skin = getSkin(_state);
-        _opacity = getOpacity(_state);
+    if (x < clip.x)
+    {
+        float dx = clip.x - x;
+        width -= dx;
+        x = clip.x;
+    }
 
-        // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
-        if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
-        {
-            _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
-            _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
-            _scrollBarRightCap = getImage("scrollBarRightCap", _state);
+    if (y < clip.y)
+    {
+        float dy = clip.y - y;
+        height -= dy;
+        y = clip.y;
+    }
+ 
+    _viewportClipBounds.set(x, y, width, height);
+    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
+                            width + border.left + padding.left + border.right + padding.right,
+                            height + border.top + padding.top + border.bottom + padding.bottom);
+    if (_clearBounds.isEmpty())
+    {
+        _clearBounds.set(_absoluteClipBounds);
+    }
 
-            _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
-        }
+    // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+    if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
+    {
+        _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
+        _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
+        _scrollBarRightCap = getImage("scrollBarRightCap", _state);
 
-        if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
-        {
-            _scrollBarTopCap = getImage("scrollBarTopCap", _state);
-            _scrollBarVertical = getImage("verticalScrollBar", _state);
-            _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
-        
-            _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
-        }
+        _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
+    }
 
-        GP_ASSERT(_layout);
-        if (_scroll != SCROLL_NONE)
-        {
-            updateScroll();
-        }
-        else
-        {
-            _layout->update(this, Vector2::zero());
-        }
+    if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+    {
+        _scrollBarTopCap = getImage("scrollBarTopCap", _state);
+        _scrollBarVertical = getImage("verticalScrollBar", _state);
+        _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+        
+        _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
 }
 
@@ -546,20 +546,25 @@ void Form::draw()
 
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
+        
+        // By setting needsClear to true here, an optimization meant to clear and redraw only areas of the form
+        // that have changed is disabled.  Currently, repositioning controls can result in areas of the screen being cleared
+        // after another control has been drawn there.  This should probably be done in two passes -- one to clear areas where
+        // dirty controls were last frame, and another to draw them where they are now.
         Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height),
-                        _skin != NULL, false, _bounds.height);
+                        /*_skin != NULL*/ true, false, _bounds.height);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
-        // restore the previous game viewport
+        // Restore the previous game viewport.
         game->setViewport(prevViewport);
         // Rebind the previous framebuffer and game viewport.
         previousFrameBuffer->bind();
     }
 
-    // Draw either with a 3D quad or sprite batch
+    // Draw either with a 3D quad or sprite batch.
     if (_node)
     {
-         // If we have the node set, then draw a 3D quad model
+         // If we have the node set, then draw a 3D quad model.
         _nodeQuad->draw();
     }
     else
@@ -581,11 +586,25 @@ const char* Form::getType() const
     return "form";
 }
 
+void Form::updateInternal(float elapsedTime)
+{
+    size_t size = __forms.size();
+    for (size_t i = 0; i < size; ++i)
+    {
+        Form* form = __forms[i];
+        GP_ASSERT(form);
+
+        if (form->isEnabled() && form->isVisible())
+        {
+            form->update(elapsedTime);
+        }
+    }
+}
+
 bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     // Check for a collision with each Form in __forms.
     // Pass the event on.
-    bool eventConsumed = false;
     size_t size = __forms.size();
     for (size_t i = 0; i < size; ++i)
     {
@@ -607,7 +626,8 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
                          point.y >= bounds.y &&
                          point.y <= bounds.y + bounds.height))
                     {
-                        eventConsumed |= form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex);
+                        if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
+                            return true;
                     }
                 }
             }
@@ -623,12 +643,13 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
                         y <= bounds.y + bounds.height))
                 {
                     // Pass on the event's position relative to the form.
-                    eventConsumed |= form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex);
+                    if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
+                        return true;
                 }
             }
         }
     }
-    return eventConsumed;
+    return false;
 }
 
 bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
@@ -638,7 +659,7 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     {
         Form* form = __forms[i];
         GP_ASSERT(form);
-        if (form->isEnabled() && form->isVisible())
+        if (form->isEnabled() && form->isVisible() && form->getState() == Control::FOCUS)
         {
             if (form->keyEvent(evt, key))
                 return true;
@@ -649,8 +670,6 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
 
 bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 {
-    bool eventConsumed = false;
-
     for (size_t i = 0; i < __forms.size(); ++i)
     {
         Form* form = __forms[i];
@@ -674,7 +693,8 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
                          point.y >= bounds.y &&
                          point.y <= bounds.y + bounds.height))
                     {
-                        eventConsumed |= form->mouseEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, wheelDelta);
+                        if (form->mouseEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, wheelDelta))
+                            return true;
                     }
                 }
             }
@@ -693,12 +713,30 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
                         y <= bounds.y + bounds.height))
                 {
                     // Pass on the event's position relative to the form.
-                    eventConsumed |= form->mouseEvent(evt, x - bounds.x, y - bounds.y, wheelDelta);
+                    if (form->mouseEvent(evt, x - bounds.x, y - bounds.y, wheelDelta))
+                        return true;
                 }
             }
         }
     }
-    return eventConsumed;
+    return false;
+}
+
+bool Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    for (size_t i = 0; i < __forms.size(); ++i)
+    {
+        Form* form = __forms[i];
+        GP_ASSERT(form);
+
+        if (form->isEnabled() && form->isVisible() && form->getState() == FOCUS)
+        {
+            if (form->gamepadEvent(evt, gamepad, analogIndex))
+                return true;
+        }
+    }
+
+    return false;
 }
 
 bool Form::projectPoint(int x, int y, Vector3* point)
@@ -710,21 +748,19 @@ bool Form::projectPoint(int x, int y, Vector3* point)
     {
         // Get info about the form's position.
         Matrix m = _node->getWorldMatrix();
-        Vector3 min(0, 0, 0);
-        m.transformPoint(&min);
+        Vector3 pointOnPlane(0, 0, 0);
+        m.transformPoint(&pointOnPlane);
 
         // Unproject point into world space.
         Ray ray;
         camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
 
         // Find the quad's plane.  We know its normal is the quad's forward vector.
-        Vector3 normal = _node->getForwardVectorWorld();
+        Vector3 normal = _node->getForwardVectorWorld().normalize();
 
-        // To get the plane's distance from the origin, we'll find the distance from the plane defined
-        // by the quad's forward vector and one of its points to the plane defined by the same vector and the origin.
-        const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
-        const float d = -(a*min.x) - (b*min.y) - (c*min.z);
-        const float distance = fabs(d) /  sqrt(a*a + b*b + c*c);
+        // To get the plane's distance from the origin, we project a point on the
+        // plane onto the plane's normal vector.
+        const float distance = fabs(Vector3::dot(pointOnPlane, normal));
         Plane plane(normal, -distance);
 
         // Check for collision with plane.

+ 17 - 0
gameplay/src/Form.h

@@ -9,6 +9,7 @@
 #include "Touch.h"
 #include "Keyboard.h"
 #include "Mouse.h"
+#include "Gamepad.h"
 
 namespace gameplay
 {
@@ -48,6 +49,8 @@ class Theme;
 class Form : public Container
 {
     friend class Platform;
+    friend class Game;
+    friend class Gamepad;
 
 public:
 
@@ -188,6 +191,11 @@ private:
      */
     void updateBounds();
 
+    /**
+     * Updates all visible, enabled forms.
+     */
+    static void updateInternal(float elapsedTime);
+
     /**
      * Propagate touch events to enabled forms.
      *
@@ -211,6 +219,15 @@ private:
      */
     static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Propagate gamepad events to enabled forms.
+     *
+     * @return True if the gamepad event is consumed, false otherwise.
+     *
+     * @see Control::gamepadEvent
+     */
+    static bool gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Get the next highest power of two of an integer.  Used when creating framebuffers.
      *

+ 23 - 2
gameplay/src/FrameBuffer.cpp

@@ -14,7 +14,7 @@ FrameBuffer* FrameBuffer::_currentFrameBuffer = NULL;
 
 FrameBuffer::FrameBuffer(const char* id, unsigned int width, unsigned int height, FrameBufferHandle handle) :
     _id(id ? id : ""), _width(width), _height(height), _handle(handle), 
-    _renderTargets(NULL), _depthStencilTarget(NULL)
+    _renderTargets(NULL), _renderTargetCount(0), _depthStencilTarget(NULL)
 {
 }
 
@@ -162,7 +162,11 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
         return;
 
     // Release our reference to the current RenderTarget at this index.
-    SAFE_RELEASE(_renderTargets[index]);
+    if (_renderTargets[index])
+    {
+        SAFE_RELEASE(_renderTargets[index]);
+        --_renderTargetCount;
+    }
 
     _renderTargets[index] = target;
 
@@ -170,6 +174,8 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
     {
         GP_ASSERT( _renderTargets[index]->getTexture() );
 
+        ++_renderTargetCount;
+
         // This FrameBuffer now references the RenderTarget.
         target->addRef();
 
@@ -199,6 +205,11 @@ RenderTarget* FrameBuffer::getRenderTarget(unsigned int index) const
     return NULL;
 }
 
+unsigned int FrameBuffer::getRenderTargetCount() const
+{
+    return _renderTargetCount;
+}
+
 void FrameBuffer::setDepthStencilTarget(DepthStencilTarget* target)
 {
     if (_depthStencilTarget == target)
@@ -245,6 +256,11 @@ DepthStencilTarget* FrameBuffer::getDepthStencilTarget() const
     return _depthStencilTarget;
 }
 
+bool FrameBuffer::isDefault() const
+{
+    return (this == _defaultFrameBuffer);
+}
+
 FrameBuffer* FrameBuffer::bind()
 {
     GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
@@ -260,4 +276,9 @@ FrameBuffer* FrameBuffer::bindDefault()
     return _defaultFrameBuffer;
 }
 
+FrameBuffer* FrameBuffer::getCurrent()
+{
+    return _currentFrameBuffer;
+}
+
 }

+ 22 - 0
gameplay/src/FrameBuffer.h

@@ -113,6 +113,13 @@ public:
      * @return The RenderTarget attached at the specified index.
      */
     RenderTarget* getRenderTarget(unsigned int index = 0) const;
+
+    /**
+     * Returns the current number of render targets attached to this frame buffer.
+     *
+     * @return The number of render targets attached.
+     */
+    unsigned int getRenderTargetCount() const;
  
     /**
      * Set this FrameBuffer's DepthStencilTarget.
@@ -128,6 +135,13 @@ public:
      */
     DepthStencilTarget* getDepthStencilTarget() const;
  
+    /**
+     * Determines whether this is the default frame bufffer.
+     *
+     * @return true if this is the default frame buffer, false otherwise.
+     */
+    bool isDefault() const;
+
     /**
      * Binds this FrameBuffer for off-screen rendering and return you the curently bound one.
      *
@@ -143,6 +157,13 @@ public:
      * @ return The default framebuffer.
      */
     static FrameBuffer* bindDefault(); 
+
+    /**
+     * Gets the currently bound FrameBuffer.
+     *
+     * @return The currently bound FrameBuffer.
+     */
+    static FrameBuffer* getCurrent();
      
 private:
 
@@ -172,6 +193,7 @@ private:
     unsigned int _height;
     FrameBufferHandle _handle;
     RenderTarget** _renderTargets;
+    unsigned int _renderTargetCount;
     DepthStencilTarget* _depthStencilTarget;
 
     static unsigned int _maxRenderTargets;

+ 16 - 4
gameplay/src/Frustum.cpp

@@ -62,6 +62,12 @@ void Frustum::getMatrix(Matrix* dst) const
 }
 
 void Frustum::getCorners(Vector3* corners) const
+{
+    getNearCorners(corners);
+    getFarCorners(corners + 4);
+}
+
+void Frustum::getNearCorners(Vector3* corners) const
 {
     GP_ASSERT(corners);
 
@@ -69,10 +75,16 @@ void Frustum::getCorners(Vector3* corners) const
     Plane::intersection(_near, _left, _bottom, &corners[1]);
     Plane::intersection(_near, _right, _bottom, &corners[2]);
     Plane::intersection(_near, _right, _top, &corners[3]);
-    Plane::intersection(_far, _right, _top, &corners[4]);
-    Plane::intersection(_far, _right, _bottom, &corners[5]);
-    Plane::intersection(_far, _left, _bottom, &corners[6]);
-    Plane::intersection(_far, _left, _top, &corners[7]);
+}
+
+void Frustum::getFarCorners(Vector3* corners) const
+{
+    GP_ASSERT(corners);
+
+    Plane::intersection(_far, _right, _top, &corners[0]);
+    Plane::intersection(_far, _right, _bottom, &corners[1]);
+    Plane::intersection(_far, _left, _bottom, &corners[2]);
+    Plane::intersection(_far, _left, _top, &corners[3]);
 }
 
 bool Frustum::intersects(const Vector3& point) const

+ 21 - 1
gameplay/src/Frustum.h

@@ -109,10 +109,30 @@ public:
      * (N-near, F-far, L-left, R-right, B-bottom, T-top)
      * LTN, LBN, RBN, RTN, RTF, RBF, LBF, LTF.
      * 
-     * @param corners The array to store the corners in.
+     * @param corners The array (of at least size 8) to store the corners in.
      */
     void getCorners(Vector3* corners) const;
 
+    /**
+     * Gets the corners of the frustum's near plane in the specified array.
+     *
+     * The corners are stored in the following order:
+     * left-top, left-bottom, right-bottom, right-top.
+     *
+     * @param corners The array (of at least size 4) to store the corners in.
+     */
+    void getNearCorners(Vector3* corners) const;
+
+    /**
+     * Gets the corners of the frustum's far plane in the specified array.
+     *
+     * The corners are stored in the following order:
+     * right-top, right-bottom, left-bottom, left-top.
+     *
+     * @param corners The array (of at least size 4) to store the corners in.
+     */
+    void getFarCorners(Vector3* corners) const;
+
     /**
      * Tests whether this frustum intersects the specified point.
      *

+ 56 - 21
gameplay/src/Game.cpp

@@ -86,6 +86,7 @@ int Game::run()
         shutdown();
         return -2;
     }
+
     return 0;
 }
 
@@ -122,30 +123,22 @@ bool Game::startup()
         Properties* scripts = _properties->getNamespace("scripts", true);
         if (scripts)
         {
-            const char* name;
-            while ((name = scripts->getNextProperty()) != NULL)
+            const char* callback;
+            while ((callback = scripts->getNextProperty()) != NULL)
             {
-                ScriptController::ScriptCallback callback = ScriptController::toCallback(name);
-                if (callback != ScriptController::INVALID_CALLBACK)
+                std::string url = scripts->getString();
+                std::string file;
+                std::string id;
+                splitURL(url, &file, &id);
+
+                if (file.size() <= 0 || id.size() <= 0)
                 {
-                    std::string url = scripts->getString();
-                    std::string file;
-                    std::string id;
-                    splitURL(url, &file, &id);
-
-                    if (file.size() <= 0 || id.size() <= 0)
-                    {
-                        GP_ERROR("Invalid %s script callback function '%s'.", name, url.c_str());
-                    }
-                    else
-                    {
-                        _scriptController->loadScript(file.c_str());
-                        _scriptController->registerCallback(callback, id);
-                    }
+                    GP_ERROR("Invalid %s script callback function '%s'.", callback, url.c_str());
                 }
                 else
                 {
-                    // Ignore everything else.
+                    _scriptController->loadScript(file.c_str());
+                    _scriptController->registerCallback(callback, id.c_str());
                 }
             }
         }
@@ -258,20 +251,41 @@ void Game::resume()
 
 void Game::exit()
 {
-	// Schedule a call to shutdown rather than calling it right away.
+    // Only perform a full/clean shutdown if FORCE_CLEAN_SHUTDOWN or
+    // GAMEPLAY_MEM_LEAK_DETECTION is defined. Every modern OS is able to
+    // handle reclaiming process memory hundreds of times faster than it
+    // would take us to go through every pointer in the engine and release
+    // them nicely. For large games, shutdown can end up taking long time,
+    // so we'll just call ::exit(0) to force an instant shutdown.
+
+#if defined FORCE_CLEAN_SHUTDOWN || defined GAMEPLAY_MEM_LEAK_DETECTION
+
+    // Schedule a call to shutdown rather than calling it right away.
 	// This handles the case of shutting down the script system from
 	// within a script function (which can cause errors).
 	static ShutdownListener listener;
 	schedule(0, &listener);
+
+#else
+
+    // End the process immediately without a full shutdown
+    ::exit(0);
+
+#endif
 }
 
+
 void Game::frame()
 {
     if (!_initialized)
     {
+        // Perform lazy first time initialization
         initialize();
         _scriptController->initializeGame();
         _initialized = true;
+
+        // Fire first game resize event
+        Platform::resizeEventInternal(_width, _height);
     }
 
 	static double lastFrameTime = Game::getGameTime();
@@ -300,9 +314,15 @@ void Game::frame()
         // Update AI.
         _aiController->update(elapsedTime);
 
+        // Update gamepads.
+        Gamepad::updateInternal(elapsedTime);
+
         // Application Update.
         update(elapsedTime);
 
+        // Update forms.
+        Form::updateInternal(elapsedTime);
+
         // Run script update.
         _scriptController->update(elapsedTime);
 
@@ -326,9 +346,15 @@ void Game::frame()
     }
 	else if (_state == Game::PAUSED)
     {
+        // Update gamepads.
+        Gamepad::updateInternal(0);
+
         // Application Update.
         update(0);
 
+        // Update forms.
+        Form::updateInternal(0);
+
         // Script update.
         _scriptController->update(0);
 
@@ -447,6 +473,10 @@ bool Game::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
     return false;
 }
 
+void Game::resizeEvent(unsigned int width, unsigned int height)
+{
+}
+
 bool Game::isGestureSupported(Gesture::GestureEvent evt)
 {
     return Platform::isGestureSupported(evt);
@@ -479,8 +509,13 @@ void Game::gestureTapEvent(int x, int y)
 {
 }
 
-void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
+void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+}
+
+void Game::getArguments(int* argc, char*** argv) const
 {
+    Platform::getArguments(argc, argv);
 }
 
 void Game::schedule(float timeOffset, TimeListener* timeListener, void* cookie)

+ 40 - 4
gameplay/src/Game.h

@@ -316,6 +316,17 @@ public:
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
     
+    /**
+     * Called when the game window has been resized.
+     *
+     * This method is called once the game window is created with its initial size
+     * and then again any time the game window changes size.
+     *
+     * @param width The new game window width.
+     * @param height The new game window height.
+     */
+    virtual void resizeEvent(unsigned int width, unsigned int height);
+
     /** 
      * Gets whether the current platform supports mouse input.
      *
@@ -432,13 +443,15 @@ public:
     virtual void gestureTapEvent(int x, int y);
 
     /**
-     * Gamepad callback on gamepad events. Override to receive Gamepad::CONNECTED_EVENT 
-     * and Gamepad::DISCONNECTED_EVENT.
+     * Gamepad callback on gamepad events.  Override to receive Gamepad::CONNECTED_EVENT 
+     * and Gamepad::DISCONNECTED_EVENT, and store the Gamepad* in order to poll it from update().
+     * Or, handle all gamepad input through BUTTON, JOYSTICK and TRIGGER events.
      *
      * @param evt The gamepad event that occurred.
-     * @param gamepad the gamepad the event occurred on
+     * @param gamepad The gamepad that generated the event.
+     * @param analogIndex If this is a JOYSTICK_EVENT or TRIGGER_EVENT, the index of the joystick or trigger whose value changed.
      */
-    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
+    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
 
     /**
      * Gets the current number of gamepads currently connected to the system.
@@ -464,6 +477,20 @@ public:
      */
     inline Gamepad* getGamepad(unsigned int index, bool preferPhysical = true) const;
 
+    /**
+	 * Sets whether multi-sampling is to be enabled/disabled. Default is disabled.
+	 *
+	 * @param enabled true sets multi-sampling to be enabled, false to be disabled.
+	 */
+	inline void setMultiSampling(bool enabled);
+
+	/*
+	 * Is multi-sampling enabled.
+	 *
+	 * @return true if multi-sampling is enabled.
+	 */
+	inline bool isMultiSampling() const;
+
     /**
      * Sets multi-touch is to be enabled/disabled. Default is disabled.
      *
@@ -493,6 +520,15 @@ public:
      */
     inline void getAccelerometerValues(float* pitch, float* roll);
 
+    /**
+     * Gets the command line arguments.
+     * 
+     * @param argc The number of command line arguments.
+     * @param argv The array of command line arguments.
+     * @script{ignore}
+     */
+    void getArguments(int* argc, char*** argv) const;
+
     /**
      * Schedules a time event to be sent to the given TimeListener a given number of game milliseconds from now.
      * Game time stops while the game is paused. A time offset of zero will fire the time event in the next frame.

+ 10 - 0
gameplay/src/Game.inl

@@ -96,6 +96,16 @@ inline bool Game::isCursorVisible()
     return Platform::isCursorVisible();
 }
 
+inline void Game::setMultiSampling(bool enabled)
+{
+    Platform::setMultiSampling(enabled);
+}
+
+inline bool Game::isMultiSampling() const
+{
+    return Platform::isMultiSampling();
+}
+
 inline void Game::setMultiTouch(bool enabled)
 {
     Platform::setMultiTouch(enabled);

+ 67 - 9
gameplay/src/Gamepad.cpp

@@ -3,6 +3,8 @@
 #include "Game.h"
 #include "Button.h"
 #include "Platform.h"
+#include "Form.h"
+#include "Joystick.h"
 
 namespace gameplay
 {
@@ -23,6 +25,7 @@ Gamepad::Gamepad(const char* formPath)
     for (int i = 0; i < 2; ++i)
     {
         _uiJoysticks[i] = NULL;
+        _triggers[i] = 0.0f;
     }
 
     for (int i = 0; i < 20; ++i)
@@ -36,9 +39,22 @@ Gamepad::Gamepad(const char* formPath)
 Gamepad::Gamepad(GamepadHandle handle, unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
                  unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
     : _handle(handle), _buttonCount(buttonCount), _joystickCount(joystickCount), _triggerCount(triggerCount),
-      _vendorId(vendorId), _productId(productId), _vendorString(vendorString), _productString(productString),
-      _form(NULL), _buttons(0)
+      _vendorId(vendorId), _productId(productId), _form(NULL), _buttons(0)
 {
+    if (vendorString)
+    {
+        _vendorString = vendorString;
+    }
+    
+    if (productString)
+    {
+        _productString = productString;
+    }
+
+    for (int i = 0; i < 2; ++i)
+    {
+        _triggers[i] = 0.0f;
+    }
 }
 
 Gamepad::~Gamepad()
@@ -173,6 +189,19 @@ Gamepad* Gamepad::getGamepad(unsigned int index, bool preferPhysical)
     return backupVirtual;
 }
 
+Gamepad* Gamepad::getGamepad(GamepadHandle handle)
+{
+    unsigned int count = __gamepads.size();
+    for (unsigned int i = 0; i < count; ++i)
+    {
+        if (__gamepads[i]->_handle == handle)
+        {
+            return __gamepads[i];
+        }
+    }
+    return NULL;
+}
+
 Gamepad::ButtonMapping Gamepad::getButtonMappingFromString(const char* string)
 {
     if (strcmp(string, "A") == 0 || strcmp(string, "BUTTON_A") == 0)
@@ -242,16 +271,18 @@ const char* Gamepad::getProductString() const
 
 void Gamepad::update(float elapsedTime)
 {
-    if (_form)
+    if (!_form)
     {
-        if (_form->isEnabled())
-        {
-            _form->update(elapsedTime);
-        }
+        Platform::pollGamepadState(this);
     }
-    else
+}
+
+void Gamepad::updateInternal(float elapsedTime)
+{
+    unsigned int size = __gamepads.size();
+    for (unsigned int i = 0; i < size; ++i)
     {
-        Platform::pollGamepadState(this);
+        __gamepads[i]->update(elapsedTime);
     }
 }
 
@@ -348,4 +379,31 @@ Form* Gamepad::getForm() const
     return _form;
 }
 
+void Gamepad::setButtons(unsigned int buttons)
+{
+    if (buttons != _buttons)
+    {
+        _buttons = buttons;
+        Platform::gamepadEventInternal(BUTTON_EVENT, this);
+    }
+}
+
+void Gamepad::setJoystickValue(unsigned int index, float x, float y)
+{
+    if (_joysticks[index].x != x || _joysticks[index].y != y)
+    {
+        _joysticks[index].set(x, y);
+        Platform::gamepadEventInternal(JOYSTICK_EVENT, this, index);
+    }
+}
+
+void Gamepad::setTriggerValue(unsigned int index, float value)
+{
+    if (_triggers[index] != value)
+    {
+        _triggers[index] = value;
+        Platform::gamepadEventInternal(TRIGGER_EVENT, this, index);
+    }
+}
+
 }

+ 25 - 23
gameplay/src/Gamepad.h

@@ -1,14 +1,16 @@
 #ifndef GAMEPAD_H_
 #define GAMEPAD_H_
 
-#include "Form.h"
-#include "Joystick.h"
+#include "Vector2.h"
 
 namespace gameplay
 {
 
-class Platform;
 class Button;
+class Container;
+class Form;
+class Joystick;
+class Platform;
 
 /**
  * Defines an interface for handling gamepad input.
@@ -17,7 +19,6 @@ class Gamepad
 {
     friend class Platform;
     friend class Game;
-    friend class Control;
     friend class Button;
 
 public:
@@ -28,7 +29,10 @@ public:
     enum GamepadEvent
     {
         CONNECTED_EVENT,
-        DISCONNECTED_EVENT
+        DISCONNECTED_EVENT,
+        BUTTON_EVENT,
+        JOYSTICK_EVENT,
+        TRIGGER_EVENT
     };
 
     /**
@@ -164,7 +168,6 @@ public:
      */
     void draw();
 
-
 private:
 
     /**
@@ -188,18 +191,23 @@ private:
      */
     Gamepad(GamepadHandle handle, 
             unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-            unsigned int vendorId, unsigned int productId, 
-            const char* vendorString, const char* productString);
+            unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString);
 
     /**
      * Copy constructor.
      */
     Gamepad(const Gamepad& copy);
 
+    /** 
+     * Destructor.
+     */
+    virtual ~Gamepad();
+
+    static void updateInternal(float elapsedTime);
+
     static Gamepad* add(GamepadHandle handle, 
                         unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-                        unsigned int vendorId, unsigned int productId, 
-                        const char* vendorString, const char* productString);
+                        unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString);
 
     static Gamepad* add(const char* formPath);
 
@@ -211,20 +219,18 @@ private:
 
     static Gamepad* getGamepad(unsigned int index, bool preferPhysical = true);
 
+    static Gamepad* getGamepad(GamepadHandle handle);
+
     static ButtonMapping getButtonMappingFromString(const char* string);
 
-    /** 
-     * Destructor.
-     */
-    virtual ~Gamepad();
+    void setButtons(unsigned int buttons);
+
+    void setJoystickValue(unsigned int index, float x, float y);
+
+    void setTriggerValue(unsigned int index, float value);
     
-    /** 
-     * Binds the Joystick and Button Control object's from the specified container.
-     */
     void bindGamepadControls(Container* container);
 
-    static unsigned int getIndexFromMapping(Gamepad::ButtonMapping mapping);
-
     GamepadHandle _handle;        // The handle of the Gamepad.
     unsigned int _buttonCount;    // Number of buttons.
     unsigned int _joystickCount;  // Number of joysticks.
@@ -233,13 +239,9 @@ private:
     unsigned int _productId;
     std::string _vendorString;
     std::string _productString;
-    
-    // Data needed for virtual gamepads.
     Form* _form;
     Joystick* _uiJoysticks[2];
     Button* _uiButtons[20];
-
-    // Current gamepad state.
     unsigned int _buttons;
     Vector2 _joysticks[2];
     float _triggers[2];

+ 1 - 1
gameplay/src/Image.h

@@ -66,7 +66,7 @@ private:
      * Constructor.
      */
     Image();
-        
+
     /**
      * Destructor.
      */

+ 148 - 0
gameplay/src/ImageControl.cpp

@@ -0,0 +1,148 @@
+#include "Base.h"
+#include "ImageControl.h"
+
+namespace gameplay
+{
+
+ImageControl::ImageControl() :
+    _srcRegion(Rectangle::empty()), _dstRegion(Rectangle::empty()), _batch(NULL),
+    _tw(0.0f), _th(0.0f), _uvs(Theme::UVs::full())
+{
+}
+
+ImageControl::~ImageControl()
+{
+    SAFE_DELETE(_batch);
+}
+
+ImageControl* ImageControl::create(const char* id, Theme::Style* style)
+{
+    GP_ASSERT(style);
+
+    ImageControl* imageControl = new ImageControl();
+    if (id)
+        imageControl->_id = id;
+    imageControl->setStyle(style);
+
+    imageControl->_consumeInputEvents = false;
+    imageControl->_focusIndex = -2;
+
+    return imageControl;
+}
+
+ImageControl* ImageControl::create(Theme::Style* style, Properties* properties)
+{
+    ImageControl* imageControl = new ImageControl();
+    imageControl->initialize(style, properties);
+
+    imageControl->_consumeInputEvents = false;
+    imageControl->_focusIndex = -2;
+
+    return imageControl;
+}
+
+void ImageControl::initialize(Theme::Style* style, Properties* properties)
+{
+    GP_ASSERT(properties);
+
+    Control::initialize(style, properties);
+
+    const char* path = properties->getString("path");
+    if (path)
+    {
+        setImage(path);
+    }
+
+    if (properties->exists("srcRegion"))
+    {
+        Vector4 region;
+        properties->getVector4("srcRegion", &region);
+        setRegionSrc(region.x, region.y, region.z, region.w);
+    }
+
+    if (properties->exists("dstRegion"))
+    {
+        Vector4 region;
+        properties->getVector4("dstRegion", &region);
+        setRegionDst(region.x, region.y, region.z, region.w);
+    }
+}
+
+void ImageControl::setImage(const char* path)
+{
+    SAFE_DELETE(_batch);
+    Texture* texture = Texture::create(path);
+    _batch = SpriteBatch::create(texture);
+    _tw = 1.0f / texture->getWidth();
+    _th = 1.0f / texture->getHeight();
+    texture->release();
+}
+
+void ImageControl::setRegionSrc(float x, float y, float width, float height)
+{
+    _srcRegion.set(x, y, width, height);
+
+    _uvs.u1 = x * _tw;
+    _uvs.u2 = (x + width) * _tw;
+    _uvs.v1 = 1.0f - (y * _th);
+    _uvs.v2 = 1.0f - ((y + height) * _th);
+}
+
+void ImageControl::setRegionSrc(const Rectangle& region)
+{
+    setRegionSrc(region.x, region.y, region.width, region.height);
+}
+
+const Rectangle& ImageControl::getRegionSrc() const
+{
+    return _srcRegion;
+}
+
+void ImageControl::setRegionDst(float x, float y, float width, float height)
+{
+    _dstRegion.set(x, y, width, height);
+}
+
+void ImageControl::setRegionDst(const Rectangle& region)
+{
+    setRegionDst(region.x, region.y, region.width, region.height);
+}
+
+const Rectangle& ImageControl::getRegionDst() const
+{
+    return _dstRegion;
+}
+
+const char* ImageControl::getType() const
+{
+    return "image";
+}
+
+void ImageControl::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
+{
+    spriteBatch->finish();
+
+    // An ImageControl is not part of the texture atlas but should use the same projection matrix.
+    _batch->setProjectionMatrix(spriteBatch->getProjectionMatrix());
+
+    Vector4 color = Vector4::one();
+    color.w *= _opacity;
+
+    _batch->start();
+    if (_dstRegion.isEmpty())
+    {
+        _batch->draw(_viewportBounds.x, _viewportBounds.y, _viewportBounds.width, _viewportBounds.height,
+            _uvs.u1, _uvs.v1, _uvs.u2, _uvs.v2, color, _viewportClipBounds);
+    }
+    else
+    {
+        _batch->draw(_viewportBounds.x + _dstRegion.x, _viewportBounds.y + _dstRegion.y,
+            _dstRegion.width, _dstRegion.height,
+            _uvs.u1, _uvs.v1, _uvs.u2, _uvs.v2, color, _viewportClipBounds);
+    }
+    _batch->finish();
+
+    spriteBatch->start();
+}
+
+}

+ 146 - 0
gameplay/src/ImageControl.h

@@ -0,0 +1,146 @@
+#ifndef IMAGECONTROL_H_
+#define IMAGECONTROL_H_
+
+#include "Button.h"
+#include "Theme.h"
+#include "Image.h"
+#include "SpriteBatch.h"
+#include "Rectangle.h"
+
+namespace gameplay
+{
+
+/**
+ * An ImageControl allows forms to display images from arbitrary files not specified in the theme.
+ *
+ * The following properties are available for image controls:
+
+ @verbatim
+     image <control ID>
+     {
+         style          = <styleID>
+         alignment      = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position       = <x, y>
+         autoWidth      = <bool>
+         autoHeight     = <bool>
+         size           = <width, height>
+         width          = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height         = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         consumeEvents  = <bool>    // Whether the label propagates input events to the Game's input event handler. Default is true.
+         path           = <string>  // Path to image or texture atlas.
+         srcRegion      = <x, y, width, height>  // Region within file to create UVs from.
+         dstRegion      = <x, y, width, height>  // Region of control's viewport to draw into.
+     }
+ @endverbatim
+ */
+class ImageControl : public Button
+{
+    friend class Container;
+
+public:
+
+    /**
+     * Create a new ImageControl.
+     *
+     * @param id The control's ID.
+     * @param style The control's style.
+     *
+     * @return The new ImageControl.
+     * @script{create}
+     */
+    static ImageControl* create(const char* id, Theme::Style* style);
+
+    /**
+     * Set the path of the image for this ImageControl to display.
+     *
+     * @param path The path to the image.
+     */
+    void setImage(const char* path);
+
+    /**
+     * Set the source region of this ImageControl.  This is the region of the file,
+     * in pixels, to use when drawing.
+     *
+     * @param x The x coordinate of the source region.
+     * @param y The y coordinate of the source region.
+     * @param width The width of the source region.
+     * @param height The height of the source region.
+     */
+    void setRegionSrc(float x, float y, float width, float height);
+
+    /**
+     * Set the source region of this ImageControl.  This is the region of the file,
+     * in pixels, to use when drawing.
+     *
+     * @param region The new source region.
+     */
+    void setRegionSrc(const Rectangle& region);
+
+    /**
+     * Get the source region of this ImageControl.
+     *
+     * @return The source region of this ImageControl.
+     */
+    const Rectangle& getRegionSrc() const;
+
+    /**
+     * Sets the destination region of this ImageControl.  This is the region
+     * within the control's viewport to draw the image.
+     *
+     * @param x The x coordinate of the destination region.
+     * @param y The y coordinate of the destination region.
+     * @param width The width of the destination region.
+     * @param height The height of the destination region.
+     */
+    void setRegionDst(float x, float y, float width, float height);
+    
+    /**
+     * Sets the destination region of this ImageControl.  This is the region
+     * within the control's viewport to draw the image.
+     *
+     * @param region The new destination region.
+     */
+    void setRegionDst(const Rectangle& region);
+
+    /**
+     * Get the destination region of this ImageControl.
+     *
+     * @return The destination region of this ImageControl.
+     */
+    const Rectangle& getRegionDst() const;
+
+    const char* getType() const;
+
+protected:
+
+    ImageControl();
+    
+    virtual ~ImageControl();
+
+    static ImageControl* create(Theme::Style* style, Properties* properties);
+
+    virtual void initialize(Theme::Style* style, Properties* properties);
+
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
+
+    // Source region.
+    Rectangle _srcRegion;
+    // Destination region.
+    Rectangle _dstRegion;
+    SpriteBatch* _batch;
+    
+    // One over texture width and height, for use when calculating UVs from a new source region.
+    float _tw;
+    float _th;
+    
+    // Calculated UVs.
+    Theme::UVs _uvs;
+
+private:
+
+    ImageControl(const ImageControl& copy);
+};
+
+}
+
+#endif

+ 89 - 3
gameplay/src/Joint.cpp

@@ -6,7 +6,7 @@ namespace gameplay
 {
 
 Joint::Joint(const char* id)
-    : Node(id), _jointMatrixDirty(true), _skinCount(0)
+    : Node(id), _jointMatrixDirty(true)
 {
 }
 
@@ -25,7 +25,6 @@ Node* Joint::cloneSingleNode(NodeCloneContext &context) const
     GP_ASSERT(copy);
     context.registerClonedNode(this, copy);
     copy->_bindPose = _bindPose;
-    copy->_skinCount = _skinCount;
     Node::cloneInto(copy, context);
     return copy;
 }
@@ -35,6 +34,27 @@ Node::Type Joint::getType() const
     return Node::JOINT;
 }
 
+Scene* Joint::getScene() const
+{
+    // Overrides Node::getScene() to search the node our skins.
+    for (const SkinReference* itr = &_skin; itr && itr->skin; itr = itr->next)
+    {
+        Model* model = itr->skin ? itr->skin->getModel() : NULL;
+        if (model)
+        {
+            Node* node = model->getNode();
+            if (node)
+            {
+                Scene* scene = node->getScene();
+                if (scene)
+                    return scene;
+            }
+        }
+    }
+
+    return Node::getScene();
+}
+
 void Joint::transformChanged()
 {
     Node::transformChanged();
@@ -47,7 +67,7 @@ void Joint::updateJointMatrix(const Matrix& bindShape, Vector4* matrixPalette)
     // the _jointMatrixDirty optimization since updateJointMatrix() may be
     // called multiple times a frame with different bindShape matrices (and
     // different matrixPallete pointers).
-    if (_skinCount > 1 || _jointMatrixDirty)
+    if (_skin.next || _jointMatrixDirty)
     {
         _jointMatrixDirty = false;
 
@@ -73,4 +93,70 @@ void Joint::setInverseBindPose(const Matrix& m)
     _jointMatrixDirty = true;
 }
 
+void Joint::addSkin(MeshSkin* skin)
+{
+    if (!_skin.skin)
+    {
+        // Store skin in root reference
+        _skin.skin = skin;
+    }
+    else
+    {
+        // Add a new SkinReference to the end of our list
+        SkinReference* ref = &_skin;
+        while (ref->next)
+        {
+            ref = ref->next;
+        }
+        ref->next = new SkinReference();
+        ref->next->skin = skin;
+    }
+}
+
+void Joint::removeSkin(MeshSkin* skin)
+{
+    if (_skin.skin == skin)
+    {
+        // Skin is our root referenced skin
+        _skin.skin = NULL;
+
+        // Shift the next skin reference down to the root
+        if (_skin.next)
+        {
+            SkinReference* tmp = _skin.next;
+            _skin.skin = tmp->skin;
+            _skin.next = tmp->next;
+            tmp->next = NULL; // prevent deletion
+            SAFE_DELETE(tmp);
+        }
+    }
+    else
+    {
+        // Search for the entry referencing this skin
+        SkinReference* ref = &_skin;
+        while (SkinReference* tmp = ref->next)
+        {
+            if (tmp->skin == skin)
+            {
+                // Link this refernce out
+                ref->next = tmp->next;
+                tmp->next = NULL; // prevent deletion
+                SAFE_DELETE(tmp);
+                break;
+            }
+            ref = tmp;
+        }
+    }
+}
+
+Joint::SkinReference::SkinReference()
+    : skin(NULL), next(NULL)
+{
+}
+
+Joint::SkinReference::~SkinReference()
+{
+    SAFE_DELETE(next);
+}
+
 }

+ 26 - 7
gameplay/src/Joint.h

@@ -25,6 +25,11 @@ public:
      */
     Node::Type getType() const;
 
+    /**
+     * @see Node::getScene()
+     */
+    Scene* getScene() const;
+
     /**
      * Returns the inverse bind pose matrix for this joint.
      * 
@@ -85,6 +90,18 @@ protected:
 
 private:
 
+    /**
+     * Internal structure to track mesh skins referencing a joint.
+     */
+    struct SkinReference
+    {
+        MeshSkin* skin;
+        SkinReference* next;
+
+        SkinReference();
+        ~SkinReference();
+    };
+
     /**
      * Constructor.
      */
@@ -95,22 +112,24 @@ private:
      */
     Joint& operator=(const Joint&);
 
-protected:
+    void addSkin(MeshSkin* skin);
+
+    void removeSkin(MeshSkin* skin);
 
     /** 
      * The Matrix representation of the Joint's bind pose.
      */
     Matrix _bindPose;
-    
-    /** 
+
+    /**
      * Flag used to mark if the Joint's matrix is dirty.
      */
     bool _jointMatrixDirty;
-    
-    /** 
-     * The number of MeshSkin's influencing the Joint.
+
+    /**
+     * Linked list of mesh skins that are referenced by this joint.
      */
-    unsigned int _skinCount;
+    SkinReference _skin;
 };
 
 }

+ 6 - 8
gameplay/src/Joystick.cpp

@@ -110,7 +110,7 @@ void Joystick::initialize(Theme::Style* style, Properties* properties)
 
 void Joystick::addListener(Control::Listener* listener, int eventFlags)
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
     }
@@ -130,7 +130,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 float dy = 0.0f;
 
                 _contactIndex = (int) contactIndex;
-                notifyListeners(Listener::PRESS);
+                notifyListeners(Control::Listener::PRESS);
 
                 // Get the displacement of the touch from the centre.
                 if (!_relative)
@@ -168,7 +168,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                     _value.set(value);
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
 
                 _state = ACTIVE;
@@ -203,7 +203,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                     _value.set(value);
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
 
                 return _consumeInputEvents;
@@ -216,7 +216,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
             {
                 _contactIndex = INVALID_CONTACT_INDEX;
 
-                notifyListeners(Listener::RELEASE);
+                notifyListeners(Control::Listener::RELEASE);
 
                 // Reset displacement and direction vectors.
                 _displacement.set(0.0f, 0.0f);
@@ -225,7 +225,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                     _value.set(value);
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
 
                 _state = NORMAL;
@@ -242,7 +242,6 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
 void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     GP_ASSERT(spriteBatch);
-    spriteBatch->start();
 
     // If the joystick is not absolute, then only draw if it is active.
     if (!_relative || (_relative && _state == ACTIVE))
@@ -284,7 +283,6 @@ void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
                 spriteBatch->draw(position.x, position.y, _innerSize->x, _innerSize->y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
         }
     }
-    spriteBatch->finish();
 }
 
 const char* Joystick::getType() const

+ 9 - 7
gameplay/src/Label.cpp

@@ -21,6 +21,12 @@ Label* Label::create(const char* id, Theme::Style* style)
         label->_id = id;
     label->setStyle(style);
 
+    // Labels don't consume input events by default like other controls.
+    label->_consumeInputEvents = false;
+
+    // Ensure that labels cannot receive focus.
+    label->_focusIndex = -2;
+
     return label;
 }
 
@@ -28,9 +34,8 @@ Label* Label::create(Theme::Style* style, Properties* properties)
 {
     Label* label = new Label();
     label->initialize(style, properties);
+
     label->_consumeInputEvents = false;
-    
-    // Ensure that labels cannot receive focus.
     label->_focusIndex = -2;
 
     return label;
@@ -41,7 +46,6 @@ void Label::initialize(Theme::Style* style, Properties* properties)
     GP_ASSERT(properties);
 
     Control::initialize(style, properties);
-
     const char* text = properties->getString("text");
     if (text)
     {
@@ -51,11 +55,11 @@ void Label::initialize(Theme::Style* style, Properties* properties)
 
 void Label::addListener(Control::Listener* listener, int eventFlags)
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
     }
-    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
+    if ((eventFlags & Control::Listener::VALUE_CHANGED) == Control::Listener::VALUE_CHANGED)
     {
         GP_ERROR("VALUE_CHANGED event is not applicable to this control.");
     }
@@ -102,8 +106,6 @@ void Label::drawText(const Rectangle& clip)
         _font->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_viewportClipBounds);
         _font->finish();
     }
-
-    _dirty = false;
 }
 
 const char* Label::getType() const

+ 85 - 0
gameplay/src/Light.cpp

@@ -72,6 +72,85 @@ Light* Light::createSpot(float red, float green, float blue, float range, float
     return new Light(SPOT, Vector3(red, green, blue), range, innerAngle, outerAngle);
 }
 
+Light* Light::create(Properties* properties)
+{
+    GP_ASSERT(properties);
+
+    // Read light type
+    std::string typeStr;
+    if (properties->exists("type"))
+        typeStr = properties->getString("type");
+    Light::Type type;
+    if (typeStr == "DIRECTIONAL")
+    {
+        type = Light::DIRECTIONAL;
+    }
+    else if (typeStr == "POINT")
+    {
+        type = Light::POINT;
+    }
+    else if (typeStr == "SPOT")
+    {
+        type = Light::SPOT;
+    }
+    else
+    {
+        GP_ERROR("Invalid 'type' parameter for light definition.");
+        return NULL;
+    }
+
+    // Read common parameters
+    Vector3 color;
+    if (!properties->getVector3("color", &color))
+    {
+        GP_ERROR("Missing valid 'color' parameter for light definition.");
+        return NULL;
+    }
+
+    // Read light-specific parameters
+    Light* light = NULL;
+    switch (type)
+    {
+    case DIRECTIONAL:
+        light = createDirectional(color);
+        break;
+    case POINT:
+        {
+            float range = properties->getFloat("range");
+            if (range == 0.0f)
+            {
+                GP_ERROR("Missing valid 'range' parameter for point light definition.");
+                return NULL;
+            }
+            light = createPoint(color, range);
+        }
+        break;
+    case SPOT:
+            float range = properties->getFloat("range");
+            if (range == 0.0f)
+            {
+                GP_ERROR("Missing valid 'range' parameter for spot light definition.");
+                return NULL;
+            }
+            float innerAngle = properties->getFloat("innerAngle");
+            if (innerAngle == 0.0f)
+            {
+                GP_ERROR("Missing valid 'innerAngle' parameter for spot light definition.");
+                return NULL;
+            }
+            float outerAngle = properties->getFloat("outerAngle");
+            if (outerAngle == 0.0f)
+            {
+                GP_ERROR("Missing valid 'outerAngle' parameter for spot light definition.");
+                return NULL;
+            }
+            light = createSpot(color, range, innerAngle, outerAngle);
+        break;
+    }
+
+    return light;
+}
+
 Light::Type Light::getLightType() const
 {
     return _type;
@@ -173,6 +252,9 @@ void Light::setRange(float range)
         GP_ERROR("Unsupported light type (%d).", _type);
         break;
     }
+
+    if (_node)
+        _node->setBoundsDirty();
 }
 
 float Light::getRangeInverse() const
@@ -221,6 +303,9 @@ void Light::setOuterAngle(float outerAngle)
 
     _spot->outerAngle = outerAngle;
     _spot->outerAngleCos = cos(outerAngle);
+
+    if (_node)
+        _node->setBoundsDirty();
 }
     
 float Light::getInnerAngleCos()  const

+ 14 - 0
gameplay/src/Light.h

@@ -3,6 +3,7 @@
 
 #include "Ref.h"
 #include "Vector3.h"
+#include "Properties.h"
 
 namespace gameplay
 {
@@ -103,6 +104,19 @@ public:
      */
     static Light* createSpot(float red, float green, float blue, float range, float innerAngle, float outerAngle);
 
+    /**
+     * Creates a light from a properties definition.
+     *
+     * The properties object must contain a "type" parameter, specifying one of the
+     * supported Light::Type values. In addition, values must be supplied for all
+     * parameters of the corresponding light-specific creation method.
+     *
+     * @param properties The properties definition of the Light.
+     *
+     * @return The new Light.
+     */
+    static Light* create(Properties* properties);
+
     /**
      * Destructor.
      */

+ 5 - 0
gameplay/src/MaterialParameter.cpp

@@ -668,4 +668,9 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
     this->AnimationTarget::cloneInto(materialParameter, context);
 }
 
+MaterialParameter::MethodBinding::MethodBinding(MaterialParameter* param) :
+    _parameter(param), _autoBinding(false)
+{
+}
+
 }

+ 14 - 4
gameplay/src/MaterialParameter.h

@@ -243,10 +243,19 @@ private:
      */
     class MethodBinding : public Ref
     {
+        friend class RenderState;
+
     public:
+
         virtual void setValue(Effect* effect) = 0;
 
     protected:
+
+        /**
+         * Constructor.
+         */
+        MethodBinding(MaterialParameter* param);
+
         /**
          * Destructor.
          */
@@ -256,6 +265,9 @@ private:
          * Hidden copy assignment operator.
          */
         MethodBinding& operator=(const MethodBinding&);
+
+        MaterialParameter* _parameter;
+        bool _autoBinding;
     };
 
     /**
@@ -269,7 +281,6 @@ private:
         MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod);
         void setValue(Effect* effect);
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ValueMethod _valueMethod;
 
@@ -287,7 +298,6 @@ private:
         MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod);
         void setValue(Effect* effect);
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ValueMethod _valueMethod;
         CountMethod _countMethod;
@@ -363,7 +373,7 @@ void MaterialParameter::bindValue(ClassType* classInstance, ParameterType (Class
 
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodValueBinding<ClassType, ParameterType>::MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod) :
-    _parameter(param), _instance(instance), _valueMethod(valueMethod)
+    MethodBinding(param), _instance(instance), _valueMethod(valueMethod)
 {
 }
 
@@ -375,7 +385,7 @@ void MaterialParameter::MethodValueBinding<ClassType, ParameterType>::setValue(E
 
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodArrayBinding<ClassType, ParameterType>::MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod) :
-    _parameter(param), _instance(instance), _valueMethod(valueMethod), _countMethod(countMethod)
+    MethodBinding(param), _instance(instance), _valueMethod(valueMethod), _countMethod(countMethod)
 {
 }
 

+ 1 - 1
gameplay/src/Matrix.h

@@ -163,7 +163,7 @@ public:
      * and a z-coordinate ranging from 0 to 1. To obtain the viewable area (in world space) of a scene,
      * create a BoundingFrustum and pass the combined view and projection matrix to the constructor.
      *
-     * @param fieldOfView The field of view in the y direction (in radians).
+     * @param fieldOfView The field of view in the y direction (in degrees).
      * @param aspectRatio The aspect ratio, defined as view space width divided by height.
      * @param zNearPlane The distance to the near view plane.
      * @param zFarPlane The distance to the far view plane.

+ 66 - 0
gameplay/src/MeshBatch.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "MeshBatch.h"
+#include "Material.h"
 
 namespace gameplay
 {
@@ -42,6 +43,66 @@ MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveTy
     return batch;
 }
 
+void MeshBatch::add(const void* vertices, size_t size, unsigned int vertexCount, const unsigned short* indices, unsigned int indexCount)
+{
+    GP_ASSERT(vertices);
+    
+    unsigned int newVertexCount = _vertexCount + vertexCount;
+    unsigned int newIndexCount = _indexCount + indexCount;
+    if (_primitiveType == Mesh::TRIANGLE_STRIP && _vertexCount > 0)
+        newIndexCount += 2; // need an extra 2 indices for connecting strips with degenerate triangles
+    
+    // Do we need to grow the batch?
+    while (newVertexCount > _vertexCapacity || (_indexed && newIndexCount > _indexCapacity))
+    {
+        if (_growSize == 0)
+            return; // growing disabled, just clip batch
+        if (!resize(_capacity + _growSize))
+            return; // failed to grow
+    }
+    
+    // Copy vertex data.
+    GP_ASSERT(_verticesPtr);
+    unsigned int vBytes = vertexCount * _vertexFormat.getVertexSize();
+    memcpy(_verticesPtr, vertices, vBytes);
+    
+    // Copy index data.
+    if (_indexed)
+    {
+        GP_ASSERT(indices);
+        GP_ASSERT(_indicesPtr);
+
+        if (_vertexCount == 0)
+        {
+            // Simply copy values directly into the start of the index array.
+            memcpy(_indicesPtr, indices, indexCount * sizeof(unsigned short));
+        }
+        else
+        {
+            if (_primitiveType == Mesh::TRIANGLE_STRIP)
+            {
+                // Create a degenerate triangle to connect separate triangle strips
+                // by duplicating the previous and next vertices.
+                _indicesPtr[0] = *(_indicesPtr-1);
+                _indicesPtr[1] = _vertexCount;
+                _indicesPtr += 2;
+            }
+            
+            // Loop through all indices and insert them, with their values offset by
+            // 'vertexCount' so that they are relative to the first newly inserted vertex.
+            for (unsigned int i = 0; i < indexCount; ++i)
+            {
+                _indicesPtr[i] = indices[i] + _vertexCount;
+            }
+        }
+        _indicesPtr += indexCount;
+        _indexCount = newIndexCount;
+    }
+    
+    _verticesPtr += vBytes;
+    _vertexCount = newVertexCount;
+}
+
 void MeshBatch::updateVertexAttributeBinding()
 {
     GP_ASSERT(_material);
@@ -155,6 +216,11 @@ bool MeshBatch::resize(unsigned int capacity)
 
     return true;
 }
+
+void MeshBatch::add(const float* vertices, unsigned int vertexCount, const unsigned short* indices, unsigned int indexCount)
+{
+    add(vertices, sizeof(float), vertexCount, indices, indexCount);
+}
     
 void MeshBatch::start()
 {

+ 26 - 2
gameplay/src/MeshBatch.h

@@ -2,11 +2,12 @@
 #define MESHBATCH_H_
 
 #include "Mesh.h"
-#include "Material.h"
 
 namespace gameplay
 {
 
+class Material;
+
 /**
  * Defines a class for rendering multiple mesh into a single draw call on the graphics device.
  */
@@ -90,7 +91,28 @@ public:
      * @param indexCount Number of indices (should be zero for non-indexed batches).
      */
     template <class T>
-    void add(T* vertices, unsigned int vertexCount, unsigned short* indices = NULL, unsigned int indexCount = 0);
+    void add(const T* vertices, unsigned int vertexCount, const unsigned short* indices = NULL, unsigned int indexCount = 0);
+
+    /**
+     * Adds a group of primitives to the batch.
+     *
+     * The vertex list passed in should be a pointer of floats where every X floats represent a
+     * single vertex (e.g. {x,y,z,u,v}).
+     *
+     * If the batch was created with 'indexed' set to true, then valid index data should be
+     * passed in this method. However, if 'indexed' was set to false, the indices and indexCount
+     * parameters can be omitted since only vertex data will be used.
+     *
+     * If the batch created to draw triangle strips, this method assumes that separate calls to
+     * add specify separate triangle strips. In this case, this method will automatically stitch
+     * separate triangle strips together using degenerate (zero-area) triangles.
+     *
+     * @param vertices Array of vertices.
+     * @param vertexCount Number of vertices.
+     * @param indices Array of indices into the vertex array (should be NULL for non-indexed batches).
+     * @param indexCount Number of indices (should be zero for non-indexed batches).
+     */
+    void add(const float* vertices, unsigned int vertexCount, const unsigned short* indices = NULL, unsigned int indexCount = 0);
 
     /**
      * Starts batching.
@@ -131,6 +153,8 @@ private:
      */
     MeshBatch& operator=(const MeshBatch&);
 
+    void add(const void* vertices, size_t size, unsigned int vertexCount, const unsigned short* indices, unsigned int indexCount);
+
     void updateVertexAttributeBinding();
 
     bool resize(unsigned int capacity);

+ 2 - 57
gameplay/src/MeshBatch.inl

@@ -9,65 +9,10 @@ Material* MeshBatch::getMaterial() const
 }
 
 template <class T>
-void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
+void MeshBatch::add(const T* vertices, unsigned int vertexCount, const unsigned short* indices, unsigned int indexCount)
 {
-    GP_ASSERT(vertices);
     GP_ASSERT(sizeof(T) == _vertexFormat.getVertexSize());
-    
-    unsigned int newVertexCount = _vertexCount + vertexCount;
-    unsigned int newIndexCount = _indexCount + indexCount;
-    if (_primitiveType == Mesh::TRIANGLE_STRIP && _vertexCount > 0)
-        newIndexCount += 2; // need an extra 2 indices for connecting strips with degenerate triangles
-    
-    // Do we need to grow the batch?
-    while (newVertexCount > _vertexCapacity || (_indexed && newIndexCount > _indexCapacity))
-    {
-        if (_growSize == 0)
-            return; // growing disabled, just clip batch
-        if (!resize(_capacity + _growSize))
-            return; // failed to grow
-    }
-    
-    // Copy vertex data.
-    GP_ASSERT(_verticesPtr);
-    unsigned int vBytes = vertexCount * _vertexFormat.getVertexSize();
-    memcpy(_verticesPtr, vertices, vBytes);
-    
-    // Copy index data.
-    if (_indexed)
-    {
-        GP_ASSERT(indices);
-        GP_ASSERT(_indicesPtr);
-
-        if (_vertexCount == 0)
-        {
-            // Simply copy values directly into the start of the index array.
-            memcpy(_indicesPtr, indices, indexCount * sizeof(unsigned short));
-        }
-        else
-        {
-            if (_primitiveType == Mesh::TRIANGLE_STRIP)
-            {
-                // Create a degenerate triangle to connect separate triangle strips
-                // by duplicating the previous and next vertices.
-                _indicesPtr[0] = *(_indicesPtr-1);
-                _indicesPtr[1] = _vertexCount;
-                _indicesPtr += 2;
-            }
-            
-            // Loop through all indices and insert them, with their values offset by
-            // 'vertexCount' so that they are relative to the first newly inserted vertex.
-            for (unsigned int i = 0; i < indexCount; ++i)
-            {
-                _indicesPtr[i] = indices[i] + _vertexCount;
-            }
-        }
-        _indicesPtr += indexCount;
-        _indexCount = newIndexCount;
-    }
-    
-    _verticesPtr += vBytes;
-    _vertexCount = newVertexCount;
+    add(vertices, sizeof(T), vertexCount, indices, indexCount);
 }
 
 }

+ 2 - 2
gameplay/src/MeshSkin.cpp

@@ -141,7 +141,7 @@ void MeshSkin::setJoint(Joint* joint, unsigned int index)
 
     if (_joints[index])
     {
-        _joints[index]->_skinCount--;
+        _joints[index]->removeSkin(this);
         SAFE_RELEASE(_joints[index]);
     }
 
@@ -150,7 +150,7 @@ void MeshSkin::setJoint(Joint* joint, unsigned int index)
     if (joint)
     {
         joint->addRef();
-        joint->_skinCount++;
+        joint->addSkin(this);
     }
 }
 

+ 1 - 0
gameplay/src/MeshSkin.h

@@ -21,6 +21,7 @@ class MeshSkin : public Transform::Listener
     friend class Model;
     friend class Joint;
     friend class Node;
+    friend class Scene;
 
 public:
 

+ 1 - 0
gameplay/src/Model.h

@@ -20,6 +20,7 @@ class NodeCloneContext;
 class Model : public Ref
 {
     friend class Node;
+    friend class Scene;
     friend class Mesh;
     friend class Bundle;
 

+ 42 - 8
gameplay/src/Node.cpp

@@ -126,6 +126,8 @@ void Node::addChild(Node* child)
 
     ++_childCount;
 
+    setBoundsDirty();
+
     if (_notifyHierarchyChanged)
     {
         hierarchyChanged();
@@ -376,13 +378,15 @@ unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool rec
 
 Scene* Node::getScene() const
 {
-    // Search for a scene in our parents.
-    for (Node* n = const_cast<Node*>(this); n != NULL; n = n->getParent())
+    if (_scene)
+        return _scene;
+
+    // Search our parent for the scene
+    if (_parent)
     {
-        if (n->_scene)
-        {
-            return n->_scene;
-        }
+        Scene* scene = _parent->getScene();
+        if (scene)
+            return scene;
     }
 
     return NULL;
@@ -766,6 +770,8 @@ void Node::setLight(Light* light)
             _light->addRef();
             _light->setNode(this);
         }
+
+        setBoundsDirty();
     }
 }
 
@@ -816,6 +822,8 @@ void Node::setTerrain(Terrain* terrain)
             _terrain->addRef();
             _terrain->setNode(this);
         }
+
+        setBoundsDirty();
     }
 }
 
@@ -872,7 +880,27 @@ const BoundingSphere& Node::getBoundingSphere() const
                 _bounds.merge(_model->getMesh()->getBoundingSphere());
             }
         }
-        else
+        if (_light)
+        {
+            switch (_light->getLightType())
+            {
+            case Light::POINT:
+                if (empty)
+                {
+                    _bounds.set(Vector3::zero(), _light->getRange());
+                    empty = false;
+                }
+                else
+                {
+                    _bounds.merge(BoundingSphere(Vector3::zero(), _light->getRange()));
+                }
+                break;
+            case Light::SPOT:
+                // TODO: Implement spot light bounds
+                break;
+            }
+        }
+        if (empty)
         {
             // Empty bounding sphere, set the world translation with zero radius
             worldMatrix.getTranslation(&_bounds.center);
@@ -933,7 +961,6 @@ const BoundingSphere& Node::getBoundingSphere() const
     return _bounds;
 }
 
-
 Node* Node::clone() const
 {
     NodeCloneContext context;
@@ -953,11 +980,13 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
     Node* copy = cloneSingleNode(context);
     GP_ASSERT(copy);
 
+    // Find our current last child
     Node* lastChild = NULL;
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     {
         lastChild = child;
     }
+
     // Loop through the nodes backwards because addChild adds the node to the front.
     for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     {
@@ -966,6 +995,7 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
         copy->addChild(childCopy);
         childCopy->release();
     }
+
     return copy;
 }
 
@@ -1002,6 +1032,10 @@ void Node::cloneInto(Node* node, NodeCloneContext &context) const
     }
     node->_world = _world;
     node->_bounds = _bounds;
+
+    // Note: Do not clone _userData - we can't make any assumptions about its content and how it's managed,
+    // so it's the caller's responsibility to clone user data if needed.
+
     if (_tags)
     {
         node->_tags = new std::map<std::string, std::string>(_tags->begin(), _tags->end());

+ 2 - 1
gameplay/src/Node.h

@@ -29,6 +29,7 @@ class Node : public Transform, public Ref
     friend class Scene;
     friend class Bundle;
     friend class MeshSkin;
+    friend class Light;
 
 public:
 
@@ -223,7 +224,7 @@ public:
      *
      * @return The scene.
      */
-    Scene* getScene() const;
+    virtual Scene* getScene() const;
 
     /**
      * Gets the top level node in this node's parent hierarchy.

+ 4 - 4
gameplay/src/Pass.cpp

@@ -54,10 +54,10 @@ void Pass::setVertexAttributeBinding(VertexAttributeBinding* binding)
     }
 }
 
- VertexAttributeBinding* Pass::getVertexAttributeBinding() const
- {
-     return _vaBinding;
- }
+VertexAttributeBinding* Pass::getVertexAttributeBinding() const
+{
+    return _vaBinding;
+}
 
 void Pass::bind()
 {

+ 78 - 0
gameplay/src/Platform.cpp

@@ -0,0 +1,78 @@
+// Implementation of base platform-agnostic platform functionality.
+#include "Base.h"
+#include "Platform.h"
+#include "Game.h"
+#include "ScriptController.h"
+#include "Form.h"
+
+namespace gameplay
+{
+
+void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    if (!Form::touchEventInternal(evt, x, y, contactIndex))
+    {
+        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
+        Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
+    }
+}
+
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    if (!Form::keyEventInternal(evt, key))
+    {
+        Game::getInstance()->keyEvent(evt, key);
+        Game::getInstance()->getScriptController()->keyEvent(evt, key);
+    }
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
+    }
+}
+
+void Platform::resizeEventInternal(unsigned int width, unsigned int height)
+{
+    // Update the width and height of the game
+    Game* game = Game::getInstance();
+    if (game->_width != width || game->_height != height)
+    {
+        game->_width = width;
+        game->_height = height;
+        game->resizeEvent(width, height);
+        game->getScriptController()->resizeEvent(width, height);
+    }
+}
+
+void Platform::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    if (!Form::gamepadEventInternal(evt, gamepad, analogIndex))
+    {
+        Game::getInstance()->gamepadEvent(evt, gamepad, analogIndex);
+        Game::getInstance()->getScriptController()->gamepadEvent(evt, gamepad, analogIndex);
+    }
+}
+
+void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
+                                             unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
+{
+    Gamepad::add(handle, buttonCount, joystickCount, triggerCount, vendorId, productId, vendorString, productString);
+}
+
+void Platform::gamepadEventDisconnectedInternal(GamepadHandle handle)
+{
+    Gamepad::remove(handle);
+}
+
+}

+ 37 - 2
gameplay/src/Platform.h

@@ -127,6 +127,18 @@ private:
      */
     static void sleep(long ms);
 
+    /**
+     * Set if multi-sampling is enabled on the platform.
+     *
+     * @param enabled true sets multi-sampling to be enabled, false to be disabled.
+     */
+    static void setMultiSampling(bool enabled);
+
+   /**
+    * Is multi-sampling mode enabled.
+    */
+    static bool isMultiSampling();
+
     /**
      * Set if multi-touch is enabled on the platform.
      *
@@ -195,6 +207,14 @@ private:
      * @param roll The accelerometer roll.
      */
     static void getAccelerometerValues(float* pitch, float* roll);
+
+    /**
+     * Gets the command line arguments.
+     * 
+     * @param argc The number of command line arguments.
+     * @param argv The array of command line arguments.
+     */
+    static void getArguments(int* argc, char*** argv);
     
     /**
      * Shows or hides the virtual keyboard (if supported).
@@ -273,7 +293,21 @@ public:
      */
     static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
-   /**
+    /**
+     * Internal method used only from static code in various platform implementation.
+     *
+     * @script{ignore}
+     */
+    static void resizeEventInternal(unsigned int width, unsigned int height);
+
+    /**
+     * Internal method used only from static code in various platform implementation.
+     *
+     * @script{ignore}
+     */
+    static void gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
+
+    /**
      * Internal method used only from static code in various platform implementation.
      *
      * @script{ignore}
@@ -281,7 +315,8 @@ public:
     static void gamepadEventConnectedInternal(GamepadHandle handle, unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
                                               unsigned int vendorId, unsigned int productId, 
                                               const char* vendorString, const char* productString);
-   /**
+
+    /**
      * Internal method used only from static code in various platform implementation.
      *
      * @script{ignore}

+ 1404 - 1419
gameplay/src/PlatformAndroid.cpp

@@ -1,1419 +1,1404 @@
-#ifdef __ANDROID__
-
-#include "Base.h"
-#include "Platform.h"
-#include "FileSystem.h"
-#include "Game.h"
-#include "Form.h"
-#include "ScriptController.h"
-#include <unistd.h>
-#include <android/sensor.h>
-#include <android_native_app_glue.h>
-#include <android/log.h>
-
-// Externally referenced global variables.
-struct android_app* __state;
-AAssetManager* __assetManager;
-
-static bool __initialized;
-static bool __suspended;
-static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
-static EGLContext __eglContext = EGL_NO_CONTEXT;
-static EGLSurface __eglSurface = EGL_NO_SURFACE;
-static EGLConfig __eglConfig = 0;
-static int __width;
-static int __height;
-static struct timespec __timespec;
-static double __timeStart;
-static double __timeAbsolute;
-static bool __vsync = WINDOW_VSYNC;
-static ASensorManager* __sensorManager;
-static ASensorEventQueue* __sensorEventQueue;
-static ASensorEvent __sensorEvent;
-static const ASensor* __accelerometerSensor;
-static int __orientationAngle = 90;
-static bool __multiTouch = false;
-static int __primaryTouchId = -1;
-static bool __displayKeyboard = false;
-
-// OpenGL VAO functions.
-static const char* __glExtensions;
-PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
-PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
-PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
-PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
-
-#define GESTURE_TAP_DURATION_MAX    200
-#define GESTURE_SWIPE_DURATION_MAX  400
-#define GESTURE_SWIPE_DISTANCE_MIN  50
-
-static std::bitset<3> __gestureEventsProcessed;
-
-struct TouchPointerData
-{
-    size_t pointerId;
-    bool pressed;
-    double time;
-    int x;
-    int y;
-};
-
-TouchPointerData __pointer0;
-TouchPointerData __pointer1;
-
-namespace gameplay
-{
-
-static double timespec2millis(struct timespec *a)
-{
-    GP_ASSERT(a);
-    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
-}
-
-extern void print(const char* format, ...)
-{
-    GP_ASSERT(format);
-    va_list argptr;
-    va_start(argptr, format);
-    __android_log_vprint(ANDROID_LOG_INFO, "gameplay-native-activity", format, argptr);
-    va_end(argptr);
-}
-
-static EGLenum checkErrorEGL(const char* msg)
-{
-    GP_ASSERT(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();
-    print("%s: %s.", msg, errmsg[error - EGL_SUCCESS]);
-    return error;
-}
-
-static int getRotation()
-{
-    jint rotation;
-
-    // Get the android application's activity.
-    ANativeActivity* activity = __state->activity;
-    JavaVM* jvm = __state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint res = jvm->AttachCurrentThread(&env, NULL);
-    if (res == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
-        return -1; 
-    }
-    GP_ASSERT(env);
-
-    jclass clsContext = env->FindClass("android/content/Context");
-    GP_ASSERT(clsContext != NULL);
-    jmethodID getSystemService = env->GetMethodID(clsContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-    GP_ASSERT(getSystemService != NULL);
-    jfieldID WINDOW_SERVICE_ID = env->GetStaticFieldID(clsContext, "WINDOW_SERVICE", "Ljava/lang/String;");
-    GP_ASSERT(WINDOW_SERVICE_ID != NULL);
-    jstring WINDOW_SERVICE = (jstring) env->GetStaticObjectField(clsContext, WINDOW_SERVICE_ID);
-    GP_ASSERT(WINDOW_SERVICE != NULL);
-    jobject windowManager = env->CallObjectMethod(activity->clazz, getSystemService, WINDOW_SERVICE);
-    GP_ASSERT(windowManager != NULL);
-    jclass clsWindowManager = env->FindClass("android/view/WindowManager");
-    GP_ASSERT(clsWindowManager != NULL);
-    jmethodID getDefaultDisplay = env->GetMethodID(clsWindowManager, "getDefaultDisplay", "()Landroid/view/Display;");
-    GP_ASSERT(getDefaultDisplay != NULL);
-    jobject defaultDisplay = env->CallObjectMethod(windowManager, getDefaultDisplay);
-    GP_ASSERT(defaultDisplay != NULL);
-    jclass clsDisplay = env->FindClass("android/view/Display");
-    GP_ASSERT(clsDisplay != NULL);
-    jmethodID getRotation = env->GetMethodID(clsDisplay, "getRotation", "()I");
-    GP_ASSERT(getRotation != NULL)
-    rotation =  env->CallIntMethod(defaultDisplay, getRotation);
-
-    return rotation;
-}
-
-
-// Initialized EGL resources.
-static bool initEGL()
-{
-    int samples = 0;
-    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
-    if (config)
-    {
-        samples = std::max(config->getInt("samples"), 0);
-    }
-
-    // Hard-coded to 32-bit/OpenGL ES 2.0.
-    // NOTE: EGL_SAMPLE_BUFFERS, EGL_SAMPLES and EGL_DEPTH_SIZE MUST remain at the beginning of the attribute list
-    // since they are expected to be at indices 0-5 in config fallback code later.
-    // EGL_DEPTH_SIZE is also expected to
-    EGLint eglConfigAttrs[] =
-    {
-        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
-        EGL_SAMPLES,            samples,
-        EGL_DEPTH_SIZE,         24,
-        EGL_RED_SIZE,           8,
-        EGL_GREEN_SIZE,         8,
-        EGL_BLUE_SIZE,          8,
-        EGL_ALPHA_SIZE,         8,
-        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
-    };
-
-    if (__eglDisplay == EGL_NO_DISPLAY && __eglContext == EGL_NO_CONTEXT)
-    {
-        // 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;
-        }
-
-        // Try both 24 and 16-bit depth sizes since some hardware (i.e. Tegra) does not support 24-bit depth
-        bool validConfig = false;
-        EGLint depthSizes[] = { 24, 16 };
-        for (unsigned int i = 0; i < 2; ++i)
-        {
-            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
-            eglConfigAttrs[3] = samples;
-            eglConfigAttrs[5] = depthSizes[i];
-
-            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-            {
-                validConfig = true;
-                break;
-            }
-
-            if (samples)
-            {
-                // Try lowering the MSAA sample size until we find a supported config
-                int sampleCount = samples;
-                while (sampleCount)
-                {
-                    GP_WARN("No EGL config found for depth_size=%d and samples=%d. Trying samples=%d instead.", depthSizes[i], sampleCount, sampleCount / 2);
-                    sampleCount /= 2;
-                    eglConfigAttrs[1] = sampleCount > 0 ? 1 : 0;
-                    eglConfigAttrs[3] = sampleCount;
-                    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-                    {
-                        validConfig = true;
-                        break;
-                    }
-                }
-                if (validConfig)
-                    break;
-            }
-            else
-            {
-                GP_WARN("No EGL config found for depth_size=%d.", depthSizes[i]);
-            }
-        }
-
-        if (!validConfig)
-        {
-            checkErrorEGL("eglChooseConfig");
-            goto error;
-        }
-
-        __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
-        if (__eglContext == EGL_NO_CONTEXT)
-        {
-            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);
-
-    __orientationAngle = getRotation() * 90;
-    
-    // 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;
-}
-
-static void destroyEGLSurface()
-{
-    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;
-    }
-}
-
-static void destroyEGLMain()
-{
-    destroyEGLSurface();
-
-    if (__eglContext != EGL_NO_CONTEXT)
-    {
-        eglDestroyContext(__eglDisplay, __eglContext);
-        __eglContext = EGL_NO_CONTEXT;
-    }
-
-    if (__eglDisplay != EGL_NO_DISPLAY)
-    {
-        eglTerminate(__eglDisplay);
-        __eglDisplay = EGL_NO_DISPLAY;
-    }
-}
-
-// Display the android virtual keyboard.
-static void displayKeyboard(android_app* state, bool show)
-{ 
-    // 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);
-    
-    GP_ASSERT(state && state->activity && state->activity->vm);
-
-    // Show or hide the keyboard by calling the appropriate Java method through JNI instead.
-    jint flags = 0;
-    JavaVM* jvm = state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint result = jvm->AttachCurrentThread(&env, NULL);
-    if (result == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
-        return; 
-    }
-    GP_ASSERT(env);
-
-    // Retrieves NativeActivity. 
-    jobject lNativeActivity = state->activity->clazz;
-    jclass ClassNativeActivity = env->GetObjectClass(lNativeActivity);
-
-    // Retrieves Context.INPUT_METHOD_SERVICE.
-    jclass ClassContext = env->FindClass("android/content/Context");
-    jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID(ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
-    jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField(ClassContext, FieldINPUT_METHOD_SERVICE);
-    
-    // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
-    jclass ClassInputMethodManager = env->FindClass("android/view/inputmethod/InputMethodManager");
-    jmethodID MethodGetSystemService = env->GetMethodID(ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-    jobject lInputMethodManager = env->CallObjectMethod(lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
-    
-    // Runs getWindow().getDecorView().
-    jmethodID MethodGetWindow = env->GetMethodID(ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
-    jobject lWindow = env->CallObjectMethod(lNativeActivity, MethodGetWindow);
-    jclass ClassWindow = env->FindClass("android/view/Window");
-    jmethodID MethodGetDecorView = env->GetMethodID(ClassWindow, "getDecorView", "()Landroid/view/View;");
-    jobject lDecorView = env->CallObjectMethod(lWindow, MethodGetDecorView);
-    if (show)
-    {
-        // Runs lInputMethodManager.showSoftInput(...).
-        jmethodID MethodShowSoftInput = env->GetMethodID( ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
-        jboolean result = env->CallBooleanMethod(lInputMethodManager, MethodShowSoftInput, lDecorView, flags); 
-    } 
-    else 
-    { 
-        // Runs lWindow.getViewToken() 
-        jclass ClassView = env->FindClass("android/view/View");
-        jmethodID MethodGetWindowToken = env->GetMethodID(ClassView, "getWindowToken", "()Landroid/os/IBinder;");
-        jobject lBinder = env->CallObjectMethod(lDecorView, MethodGetWindowToken); 
-        
-        // lInputMethodManager.hideSoftInput(...). 
-        jmethodID MethodHideSoftInput = env->GetMethodID(ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); 
-        jboolean lRes = env->CallBooleanMethod( lInputMethodManager, MethodHideSoftInput, lBinder, flags); 
-    }
-    
-    // Finished with the JVM.
-    jvm->DetachCurrentThread(); 
-}
-
-// Gets the Keyboard::Key enumeration constant that corresponds to the given Android key code.
-static Keyboard::Key getKey(int keycode, int metastate)
-{
-    bool shiftOn = (metastate == AMETA_SHIFT_ON);
-    
-    switch(keycode)
-    {
-        case AKEYCODE_HOME:
-            return Keyboard::KEY_HOME;
-        case AKEYCODE_0:
-            return Keyboard::KEY_ZERO;
-        case AKEYCODE_1:
-            return Keyboard::KEY_ONE;
-        case AKEYCODE_2:
-            return Keyboard::KEY_TWO;
-        case AKEYCODE_3:
-            return Keyboard::KEY_THREE;
-        case AKEYCODE_4:
-            return Keyboard::KEY_FOUR;
-        case AKEYCODE_5:
-            return Keyboard::KEY_FIVE;
-        case AKEYCODE_6:
-            return Keyboard::KEY_SIX;
-        case AKEYCODE_7:
-            return Keyboard::KEY_SEVEN;
-        case AKEYCODE_8:
-            return Keyboard::KEY_EIGHT;
-        case AKEYCODE_9:
-            return Keyboard::KEY_NINE;
-        case AKEYCODE_STAR:
-            return Keyboard::KEY_ASTERISK;
-        case AKEYCODE_POUND:
-            return Keyboard::KEY_NUMBER;
-        case AKEYCODE_DPAD_UP:
-            return Keyboard::KEY_UP_ARROW;
-        case AKEYCODE_DPAD_DOWN:
-            return Keyboard::KEY_DOWN_ARROW;
-        case AKEYCODE_DPAD_LEFT:
-            return Keyboard::KEY_LEFT_ARROW;
-        case AKEYCODE_DPAD_RIGHT:
-            return Keyboard::KEY_RIGHT_ARROW;
-        case AKEYCODE_A:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
-        case AKEYCODE_B:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
-       case AKEYCODE_C:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
-        case AKEYCODE_D:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
-        case AKEYCODE_E:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
-        case AKEYCODE_F:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
-        case AKEYCODE_G:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
-        case AKEYCODE_H:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
-        case AKEYCODE_I:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
-        case AKEYCODE_J:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
-        case AKEYCODE_K:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
-        case AKEYCODE_L:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
-        case AKEYCODE_M:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
-        case AKEYCODE_N:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
-        case AKEYCODE_O:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
-        case AKEYCODE_P:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
-        case AKEYCODE_Q:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
-        case AKEYCODE_R:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
-        case AKEYCODE_S:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
-        case AKEYCODE_T:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
-        case AKEYCODE_U:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
-        case AKEYCODE_V:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
-        case AKEYCODE_W:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
-        case AKEYCODE_X:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
-        case AKEYCODE_Y:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
-        case AKEYCODE_Z:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
-        case AKEYCODE_COMMA:
-            return Keyboard::KEY_COMMA;
-        case AKEYCODE_PERIOD:
-            return Keyboard::KEY_PERIOD;
-        case AKEYCODE_ALT_LEFT:
-        case AKEYCODE_ALT_RIGHT:
-            return Keyboard::KEY_ALT;
-        case AKEYCODE_SHIFT_LEFT:
-        case AKEYCODE_SHIFT_RIGHT:
-            return Keyboard::KEY_SHIFT;
-        case AKEYCODE_TAB:
-            return Keyboard::KEY_TAB;
-        case AKEYCODE_SPACE:
-            return Keyboard::KEY_SPACE;
-        case AKEYCODE_ENTER:
-            return Keyboard::KEY_RETURN;
-        case AKEYCODE_DEL:
-            return Keyboard::KEY_DELETE;
-        case AKEYCODE_GRAVE:
-            return Keyboard::KEY_GRAVE;
-        case AKEYCODE_MINUS:
-            return Keyboard::KEY_MINUS;
-        case AKEYCODE_EQUALS:
-            return Keyboard::KEY_EQUAL;
-        case AKEYCODE_LEFT_BRACKET:
-            return Keyboard::KEY_LEFT_BRACKET;
-        case AKEYCODE_RIGHT_BRACKET:
-            return Keyboard::KEY_RIGHT_BRACKET;
-        case AKEYCODE_BACKSLASH:
-            return Keyboard::KEY_BACK_SLASH;
-        case AKEYCODE_SEMICOLON:
-            return Keyboard::KEY_SEMICOLON;
-        case AKEYCODE_APOSTROPHE:
-            return Keyboard::KEY_APOSTROPHE;
-        case AKEYCODE_SLASH:
-            return Keyboard::KEY_SLASH;
-        case AKEYCODE_AT:
-            return Keyboard::KEY_AT;
-        case AKEYCODE_PLUS:
-            return Keyboard::KEY_PLUS;
-        case AKEYCODE_PAGE_UP:
-            return Keyboard::KEY_PG_UP;
-        case AKEYCODE_PAGE_DOWN:
-            return Keyboard::KEY_PG_DOWN;
-        case AKEYCODE_MENU:
-            return Keyboard::KEY_MENU;
-        case AKEYCODE_SEARCH:
-            return Keyboard::KEY_SEARCH;
-        default:
-            return Keyboard::KEY_NONE;
-    }
-}
-
-/**
- * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
- */
-static int getUnicode(int keycode, int metastate)
-{
-    if (keycode == AKEYCODE_DEL)
-        return 0x0008;
-    // TODO: Doesn't support unicode currently.
-    Keyboard::Key key = getKey(keycode, metastate);
-    switch (key)
-    {
-    case Keyboard::KEY_BACKSPACE:
-        return 0x0008;
-    case Keyboard::KEY_TAB:
-        return 0x0009;
-    case Keyboard::KEY_RETURN:
-    case Keyboard::KEY_KP_ENTER:
-        return 0x000A;
-    case Keyboard::KEY_ESCAPE:
-        return 0x001B;
-    case Keyboard::KEY_SPACE:
-    case Keyboard::KEY_EXCLAM:
-    case Keyboard::KEY_QUOTE:
-    case Keyboard::KEY_NUMBER:
-    case Keyboard::KEY_DOLLAR:
-    case Keyboard::KEY_PERCENT:
-    case Keyboard::KEY_CIRCUMFLEX:
-    case Keyboard::KEY_AMPERSAND:
-    case Keyboard::KEY_APOSTROPHE:
-    case Keyboard::KEY_LEFT_PARENTHESIS:
-    case Keyboard::KEY_RIGHT_PARENTHESIS:
-    case Keyboard::KEY_ASTERISK:
-    case Keyboard::KEY_PLUS:
-    case Keyboard::KEY_COMMA:
-    case Keyboard::KEY_MINUS:
-    case Keyboard::KEY_PERIOD:
-    case Keyboard::KEY_SLASH:
-    case Keyboard::KEY_ZERO:
-    case Keyboard::KEY_ONE:
-    case Keyboard::KEY_TWO:
-    case Keyboard::KEY_THREE:
-    case Keyboard::KEY_FOUR:
-    case Keyboard::KEY_FIVE:
-    case Keyboard::KEY_SIX:
-    case Keyboard::KEY_SEVEN:
-    case Keyboard::KEY_EIGHT:
-    case Keyboard::KEY_NINE:
-    case Keyboard::KEY_COLON:
-    case Keyboard::KEY_SEMICOLON:
-    case Keyboard::KEY_LESS_THAN:
-    case Keyboard::KEY_EQUAL:
-    case Keyboard::KEY_GREATER_THAN:
-    case Keyboard::KEY_QUESTION:
-    case Keyboard::KEY_AT:
-    case Keyboard::KEY_CAPITAL_A:
-    case Keyboard::KEY_CAPITAL_B:
-    case Keyboard::KEY_CAPITAL_C:
-    case Keyboard::KEY_CAPITAL_D:
-    case Keyboard::KEY_CAPITAL_E:
-    case Keyboard::KEY_CAPITAL_F:
-    case Keyboard::KEY_CAPITAL_G:
-    case Keyboard::KEY_CAPITAL_H:
-    case Keyboard::KEY_CAPITAL_I:
-    case Keyboard::KEY_CAPITAL_J:
-    case Keyboard::KEY_CAPITAL_K:
-    case Keyboard::KEY_CAPITAL_L:
-    case Keyboard::KEY_CAPITAL_M:
-    case Keyboard::KEY_CAPITAL_N:
-    case Keyboard::KEY_CAPITAL_O:
-    case Keyboard::KEY_CAPITAL_P:
-    case Keyboard::KEY_CAPITAL_Q:
-    case Keyboard::KEY_CAPITAL_R:
-    case Keyboard::KEY_CAPITAL_S:
-    case Keyboard::KEY_CAPITAL_T:
-    case Keyboard::KEY_CAPITAL_U:
-    case Keyboard::KEY_CAPITAL_V:
-    case Keyboard::KEY_CAPITAL_W:
-    case Keyboard::KEY_CAPITAL_X:
-    case Keyboard::KEY_CAPITAL_Y:
-    case Keyboard::KEY_CAPITAL_Z:
-    case Keyboard::KEY_LEFT_BRACKET:
-    case Keyboard::KEY_BACK_SLASH:
-    case Keyboard::KEY_RIGHT_BRACKET:
-    case Keyboard::KEY_UNDERSCORE:
-    case Keyboard::KEY_GRAVE:
-    case Keyboard::KEY_A:
-    case Keyboard::KEY_B:
-    case Keyboard::KEY_C:
-    case Keyboard::KEY_D:
-    case Keyboard::KEY_E:
-    case Keyboard::KEY_F:
-    case Keyboard::KEY_G:
-    case Keyboard::KEY_H:
-    case Keyboard::KEY_I:
-    case Keyboard::KEY_J:
-    case Keyboard::KEY_K:
-    case Keyboard::KEY_L:
-    case Keyboard::KEY_M:
-    case Keyboard::KEY_N:
-    case Keyboard::KEY_O:
-    case Keyboard::KEY_P:
-    case Keyboard::KEY_Q:
-    case Keyboard::KEY_R:
-    case Keyboard::KEY_S:
-    case Keyboard::KEY_T:
-    case Keyboard::KEY_U:
-    case Keyboard::KEY_V:
-    case Keyboard::KEY_W:
-    case Keyboard::KEY_X:
-    case Keyboard::KEY_Y:
-    case Keyboard::KEY_Z:
-    case Keyboard::KEY_LEFT_BRACE:
-    case Keyboard::KEY_BAR:
-    case Keyboard::KEY_RIGHT_BRACE:
-    case Keyboard::KEY_TILDE:
-        return key;
-    default:
-        return 0;
-    }
-}
-
-// Process the next input event.
-static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
-{
-    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
-    {
-        int32_t action = AMotionEvent_getAction(event);
-        size_t pointerIndex;
-        size_t pointerId;
-        size_t pointerCount;
-        int x;
-        int y;
-        
-        switch (action & AMOTION_EVENT_ACTION_MASK)
-        {
-            case AMOTION_EVENT_ACTION_DOWN:
-                {
-                    pointerId = AMotionEvent_getPointerId(event, 0);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
-
-                    // Gesture handling
-                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
-                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
-                    {
-                        __pointer0.pressed = true;
-                        __pointer0.time = Game::getInstance()->getAbsoluteTime();
-                        __pointer0.pointerId = pointerId;
-                        __pointer0.x = x;
-                        __pointer0.y = y;
-                    }
-
-                    // Primary pointer down.
-                    gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, x, y, pointerId);
-                    __primaryTouchId = pointerId;
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_UP:
-                {
-                    pointerId = AMotionEvent_getPointerId(event, 0);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
-                    
-                    // Gestures
-                    bool gestureDetected = false;
-                    if ( __pointer0.pressed &&  __pointer0.pointerId == pointerId)
-                    {
-                        int deltaX = x - __pointer0.x;
-                        int deltaY = y - __pointer0.y;
-
-                        // Test for swipe
-                        if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
-                            gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_SWIPE_DURATION_MAX && 
-                            (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
-                        {
-                            int direction = 0;
-                            if ( abs(deltaX) > abs(deltaY) )
-                            {
-                                if (deltaX > 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
-                                else if (deltaX < 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_LEFT;
-                            }
-                            else
-                            {
-                                if (deltaY > 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_DOWN;
-                                else if (deltaY < 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_UP;
-                            }
-                            gameplay::Game::getInstance()->gestureSwipeEvent(x, y, direction);
-                            __pointer0.pressed = false;
-                            gestureDetected = true;
-                        }
-                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
-                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_TAP_DURATION_MAX)
-                        {
-                            gameplay::Game::getInstance()->gestureTapEvent(x, y);
-                            __pointer0.pressed = false;
-                            gestureDetected = true;
-                        }
-                    }
-
-                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
-                    {
-                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, x, y, pointerId);
-                    }
-                    __primaryTouchId = -1;
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_POINTER_DOWN:
-                {
-                    pointerId = AMotionEvent_getPointerId(event, 0);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
-
-                    // Gesture handling
-                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
-                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
-                    {
-                        __pointer1.pressed = true;
-                        __pointer1.time = Game::getInstance()->getAbsoluteTime();
-                        __pointer1.pointerId = pointerId;
-                        __pointer1.x = x;
-                        __pointer1.y = y;
-                    }
-
-                    // Non-primary pointer down.
-                    if (__multiTouch)
-                    {
-                        pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-                        pointerId = AMotionEvent_getPointerId(event, pointerIndex);
-                        gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, AMotionEvent_getX(event, pointerIndex), AMotionEvent_getY(event, pointerIndex), pointerId);
-                    }
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_POINTER_UP:
-                {
-                    pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-                    pointerId = AMotionEvent_getPointerId(event, pointerIndex);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
-
-                    bool gestureDetected = false;
-                    if ( __pointer1.pressed &&  __pointer1.pointerId == pointerId)
-                    {
-                        int deltaX = x - __pointer1.x;
-                        int deltaY = y - __pointer1.y;
-
-                        // Test for swipe
-                        if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
-                            gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_SWIPE_DURATION_MAX && 
-                            (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
-                        {
-                            int direction;
-                            if (deltaX > 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
-                            else if (deltaX < 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_LEFT;
-                            
-                            if (deltaY > 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_DOWN;
-                            else if (deltaY < 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_UP;
-
-                            gameplay::Game::getInstance()->gestureSwipeEvent(x, y, direction);
-                            __pointer1.pressed = false;
-                            gestureDetected = true;
-                        }
-                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
-                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_TAP_DURATION_MAX)
-                        {
-                            gameplay::Game::getInstance()->gestureTapEvent(x, y);
-                            __pointer1.pressed = false;
-                            gestureDetected = true;
-                        }
-                    }
-
-                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
-                    {
-                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, AMotionEvent_getX(event, pointerIndex), AMotionEvent_getY(event, pointerIndex), pointerId);
-                    }
-                    if (__primaryTouchId == pointerId)
-                        __primaryTouchId = -1;
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_MOVE:
-                {
-                    // ACTION_MOVE events are batched, unlike the other events.
-                    pointerCount = AMotionEvent_getPointerCount(event);
-                    for (size_t i = 0; i < pointerCount; ++i)
-                    {
-                        pointerId = AMotionEvent_getPointerId(event, i);
-                        if (__multiTouch || __primaryTouchId == pointerId)
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, AMotionEvent_getX(event, i), AMotionEvent_getY(event, i), pointerId);
-                        }
-                    }
-                }
-                break;
-        }
-        return 1;
-    } 
-    else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
-    {
-        int32_t action = AKeyEvent_getAction(event);
-        int32_t keycode = AKeyEvent_getKeyCode(event);
-        int32_t metastate = AKeyEvent_getMetaState(event); 
-        
-        switch(action)
-        {
-            case AKEY_EVENT_ACTION_DOWN:
-                Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, getKey(keycode, metastate));
-                if (int character = getUnicode(keycode, metastate))
-                    gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
-                break;
-                    
-            case AKEY_EVENT_ACTION_UP:
-                gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
-                break;
-        }
-    }
-    return 0;
-}
-
-// Process the next main command.
-static void engine_handle_cmd(struct android_app* app, int32_t cmd)
-{
-    switch (cmd) 
-    {
-        case APP_CMD_INIT_WINDOW:
-            // The window is being shown, get it ready.
-            if (app->window != NULL)
-            {
-                initEGL();
-                __initialized = true;
-            }
-            break;
-        case APP_CMD_TERM_WINDOW:
-            destroyEGLSurface();
-            __initialized = false;
-            break;
-        case APP_CMD_DESTROY:
-            Game::getInstance()->exit();
-            destroyEGLMain();
-            __initialized = false;
-            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 microseconds).
-                ASensorEventQueue_setEventRate(__sensorEventQueue, __accelerometerSensor, (1000L/60)*1000);
-            }
-
-            if (Game::getInstance()->getState() == Game::UNINITIALIZED)
-            {
-                Game::getInstance()->run();
-            }
-            else
-            {
-                Game::getInstance()->resume();
-            }
-            break;
-        case APP_CMD_RESUME:
-            if (__initialized)
-            {
-                Game::getInstance()->resume();
-            }
-            __suspended = false;
-            break;
-        case APP_CMD_PAUSE:
-            Game::getInstance()->pause();
-            __suspended = true;
-            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);
-            }
-            break;
-    }
-}
-
-Platform::Platform(Game* game)
-    : _game(game)
-{
-}
-
-Platform::~Platform()
-{
-}
-
-Platform* Platform::create(Game* game, void* attachToWindow)
-{
-    Platform* platform = new Platform(game);
-    return platform;
-}
-
-int Platform::enterMessagePump()
-{
-    GP_ASSERT(__state && __state->activity && __state->activity->vm);
-
-    __initialized = false;
-    __suspended = false;
-
-    // Get the android application's activity.
-    ANativeActivity* activity = __state->activity;
-    JavaVM* jvm = __state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint res = jvm->AttachCurrentThread(&env, NULL);
-    if (res == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
-        return -1; 
-    }
-    GP_ASSERT(env);
-
-    /* Get external files directory on Android; this will result in a directory where all app files
-     * should be stored, like /mnt/sdcard/android/<package-name>/files/
-     */
-    jboolean isCopy;
-
-    jclass clazz = env->GetObjectClass(activity->clazz);
-    jmethodID methodGetExternalStorage = env->GetMethodID(clazz, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
-
-    jclass clazzFile = env->FindClass("java/io/File");
-    jmethodID methodGetPath = env->GetMethodID(clazzFile, "getPath", "()Ljava/lang/String;");
-
-    // Now has java.io.File object pointing to directory
-    jobject objectFile  = env->CallObjectMethod(activity->clazz, methodGetExternalStorage, NULL);
-    
-    // Now has String object containing path to directory
-    jstring stringExternalPath = static_cast<jstring>(env->CallObjectMethod(objectFile, methodGetPath));
-    const char* externalPath = env->GetStringUTFChars(stringExternalPath, &isCopy);
-
-    // Set the default path to store the resources.
-    std::string assetsPath(externalPath);
-    if (externalPath[strlen(externalPath)-1] != '/')
-        assetsPath += "/";
-
-    FileSystem::setResourcePath(assetsPath.c_str());    
-
-    // Release string data
-    env->ReleaseStringUTFChars(stringExternalPath, externalPath);
-    jvm->DetachCurrentThread();
-    
-    // Get the asset manager to get the resources from the .apk file.
-    __assetManager = 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;
-    
-    while (true)
-    {
-        // Read all pending events.
-        int ident;
-        int events;
-        struct android_poll_source* source;
-        
-        while ((ident=ALooper_pollAll(!__suspended ? 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)
-            {
-                return 0;
-            }
-        }
-        
-        // Idle time (no events left to process) is spent rendering.
-        // We skip rendering when the app is paused.
-        if (__initialized && !__suspended)
-        {
-            _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)
-            {
-                EGLint error = eglGetError();
-                if (error == EGL_BAD_NATIVE_WINDOW)
-                {
-                    if (__state->window != NULL)
-                    {
-                        destroyEGLSurface();
-                        initEGL();
-                    }
-                    __initialized = true;
-                }
-                else
-                {
-                    perror("eglSwapBuffers");
-                    break;
-                }
-            }
-        }
-            
-        // Display the keyboard.
-        gameplay::displayKeyboard(__state, __displayKeyboard);
-    }
-}
-
-void Platform::signalShutdown() 
-{
-    // nothing to do  
-}
-
-bool Platform::canExit()
-{
-    return true;
-}
-   
-unsigned int Platform::getDisplayWidth()
-{
-    return __width;
-}
-    
-unsigned int Platform::getDisplayHeight()
-{
-    return __height;
-}
-    
-double Platform::getAbsoluteTime()
-{
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    double now = timespec2millis(&__timespec);
-    __timeAbsolute = now - __timeStart;
-
-    return __timeAbsolute;
-}
-
-void Platform::setAbsoluteTime(double time)
-{
-    __timeAbsolute = time;
-}
-
-bool Platform::isVsync()
-{
-    return __vsync;
-}
-
-void Platform::setVsync(bool enable)
-{
-    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
-    __vsync = enable;
-}
-
-
-void Platform::swapBuffers()
-{
-    if (__eglDisplay && __eglSurface)
-        eglSwapBuffers(__eglDisplay, __eglSurface);
-}
-
-void Platform::sleep(long ms)
-{
-    usleep(ms * 1000);
-}
-
-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.
-    switch (__orientationAngle)
-    {
-    case 90:
-        tx = -__sensorEvent.acceleration.y;
-        ty = __sensorEvent.acceleration.x;
-        break;
-    case 180:
-        tx = -__sensorEvent.acceleration.x;
-        ty = -__sensorEvent.acceleration.y;
-        break;
-    case 270:
-        tx = __sensorEvent.acceleration.y;
-        ty = -__sensorEvent.acceleration.x;
-        break;
-    default:
-        tx = __sensorEvent.acceleration.x;
-        ty = __sensorEvent.acceleration.y;
-        break;
-    }
-    tz = __sensorEvent.acceleration.z;
-
-    if (pitch != NULL)
-    {
-        GP_ASSERT(tx * tx + tz * tz);
-        *pitch = -atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
-    }
-    if (roll != NULL)
-    {
-        GP_ASSERT(ty * ty + tz * tz);
-        *roll = -atan(tx / sqrt(ty * ty + tz * tz)) * 180.0f * M_1_PI;
-    }
-}
-
-bool Platform::hasMouse()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setMouseCaptured(bool captured)
-{
-    // not supported
-}
-
-bool Platform::isMouseCaptured()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setCursorVisible(bool visible)
-{
-    // not supported
-}
-
-bool Platform::isCursorVisible()
-{
-    // not supported
-    return false;
-}
-
-void Platform::displayKeyboard(bool display)
-{
-    if (display)
-        __displayKeyboard = true;
-    else
-        __displayKeyboard = false;
-}
-
-void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
-        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-        Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
-    }
-}
-
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    if (!Form::keyEventInternal(evt, key))
-    {
-        Game::getInstance()->keyEvent(evt, key);
-        Game::getInstance()->getScriptController()->keyEvent(evt, key);
-    }
-}
-
-bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
-{
-    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else
-    {
-        return Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
-    }
-}
-
-void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-                                             unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
-{
-    Gamepad::add(handle, buttonCount, joystickCount, triggerCount, vendorId, productId, vendorString, productString);
-}
-
-void Platform::gamepadEventDisconnectedInternal(GamepadHandle handle)
-{
-    Gamepad::remove(handle);
-}
-
-void Platform::shutdownInternal()
-{
-    Game::getInstance()->shutdown();
-}
-
-bool Platform::isGestureSupported(Gesture::GestureEvent evt)
-{
-    // Pinch currently not implemented
-    return evt == gameplay::Gesture::GESTURE_SWIPE || evt == gameplay::Gesture::GESTURE_TAP;
-}
-
-void Platform::registerGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.set();
-        break;
-
-    case Gesture::GESTURE_TAP:
-    case Gesture::GESTURE_SWIPE:
-        __gestureEventsProcessed.set(evt);
-        break;
-
-    default:
-        break;
-    }
-}
-
-void Platform::unregisterGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.reset();
-        break;
-
-    case Gesture::GESTURE_TAP:
-    case Gesture::GESTURE_SWIPE:
-        __gestureEventsProcessed.set(evt, 0);
-        break;
-
-    default:
-        break;
-    }
-}
-    
-bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
-{
-    return __gestureEventsProcessed.test(evt);
-}
-
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-}
-
-bool Platform::launchURL(const char *url)
-{
-    if (url == NULL || *url == '\0')
-        return false;
-
-    bool result = true;
-
-    android_app* state = __state;
-    GP_ASSERT(state && state->activity && state->activity->vm);
-    JavaVM* jvm = state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint r = jvm->AttachCurrentThread(&env, NULL);
-    if (r == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
-        return false;
-    }
-    GP_ASSERT(env);
-
-    jclass classActivity = env->FindClass("android/app/NativeActivity");
-    jclass classIntent = env->FindClass("android/content/Intent");
-    jclass classUri = env->FindClass("android/net/Uri");
-
-    GP_ASSERT(classActivity && classIntent && classUri);
-
-    // Get static field ID Intent.ACTION_VIEW
-    jfieldID fieldActionView = env->GetStaticFieldID(classIntent, "ACTION_VIEW", "Ljava/lang/String;");
-    GP_ASSERT(fieldActionView);
-
-    // Get string value of Intent.ACTION_VIEW, we'll need that to pass to Intent's constructor later on
-    jstring paramActionView = (jstring)env->GetStaticObjectField(classIntent, fieldActionView);
-    GP_ASSERT(paramActionView);
-
-    // Get method ID Uri.parse, will be needed to parse the url given into Uri object
-    jmethodID methodUriParse = env->GetStaticMethodID(classUri, "parse","(Ljava/lang/String;)Landroid/net/Uri;");
-    GP_ASSERT(methodUriParse);
-
-    // Get method ID Activity.startActivity, so we can start the appropriate activity for the View action of our Uri
-    jmethodID methodActivityStartActivity = env->GetMethodID(classActivity, "startActivity","(Landroid/content/Intent;)V");
-    GP_ASSERT(methodActivityStartActivity);
-
-    // Get method ID Intent constructor, the one that takes action and uri (String;Uri)
-    jmethodID methodIntentInit = env->GetMethodID(classIntent, "<init>","(Ljava/lang/String;Landroid/net/Uri;)V");
-    GP_ASSERT(methodIntentInit);
-
-    // Convert our url to Java's string and parse it to Uri
-    jstring paramUrlString = env->NewStringUTF(url);
-    jobject paramUri = env->CallStaticObjectMethod(classUri, methodUriParse, paramUrlString);
-    GP_ASSERT(paramUri);
-
-    // Create Intent with Intent.ACTION_VIEW and parsed Uri arguments
-    jobject paramIntent = env->NewObject(classIntent, methodIntentInit, paramActionView, paramUri);
-    GP_ASSERT(paramIntent);
-
-    // Launch NativeActivity.startActivity with our intent to view the url! state->activity->clazz holds
-    // our NativeActivity object
-    env->CallVoidMethod(state->activity->clazz, methodActivityStartActivity, paramIntent);
-
-    /* startActivity may throw a ActivitNotFoundException if, well, activity is not found.
-       Example: http://<url> is passed to the intent but there is no browser installed in the system
-       we need to handle it. */
-    jobject exception = env->ExceptionOccurred();
-
-    // We're not lucky here
-    if (exception)
-    {
-        // Print out the exception data to logcat
-        env->ExceptionDescribe();
-
-        // Exception needs to be cleared
-        env->ExceptionClear();
-
-        // Launching the url failed
-        result = false;
-    }
-
-    // See you Space Cowboy
-    jvm->DetachCurrentThread();
-    return result;
-}
-
-}
-
-#endif
+#ifdef __ANDROID__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+#include "Form.h"
+#include "ScriptController.h"
+#include <unistd.h>
+#include <android/sensor.h>
+#include <android_native_app_glue.h>
+#include <android/log.h>
+
+// Externally referenced global variables.
+struct android_app* __state;
+AAssetManager* __assetManager;
+
+static bool __initialized;
+static bool __suspended;
+static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
+static EGLContext __eglContext = EGL_NO_CONTEXT;
+static EGLSurface __eglSurface = EGL_NO_SURFACE;
+static EGLConfig __eglConfig = 0;
+static int __width;
+static int __height;
+static struct timespec __timespec;
+static double __timeStart;
+static double __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+static ASensorManager* __sensorManager;
+static ASensorEventQueue* __sensorEventQueue;
+static ASensorEvent __sensorEvent;
+static const ASensor* __accelerometerSensor;
+static int __orientationAngle = 90;
+static bool __multiSampling = false;
+static bool __multiTouch = false;
+static int __primaryTouchId = -1;
+static bool __displayKeyboard = false;
+
+// OpenGL VAO functions.
+static const char* __glExtensions;
+PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
+PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
+PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
+PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
+
+#define GESTURE_TAP_DURATION_MAX    200
+#define GESTURE_SWIPE_DURATION_MAX  400
+#define GESTURE_SWIPE_DISTANCE_MIN  50
+
+static std::bitset<3> __gestureEventsProcessed;
+
+struct TouchPointerData
+{
+    size_t pointerId;
+    bool pressed;
+    double time;
+    int x;
+    int y;
+};
+
+TouchPointerData __pointer0;
+TouchPointerData __pointer1;
+
+namespace gameplay
+{
+
+static double timespec2millis(struct timespec *a)
+{
+    GP_ASSERT(a);
+    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
+}
+
+extern void print(const char* format, ...)
+{
+    GP_ASSERT(format);
+    va_list argptr;
+    va_start(argptr, format);
+    __android_log_vprint(ANDROID_LOG_INFO, "gameplay-native-activity", format, argptr);
+    va_end(argptr);
+}
+
+static EGLenum checkErrorEGL(const char* msg)
+{
+    GP_ASSERT(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();
+    print("%s: %s.", msg, errmsg[error - EGL_SUCCESS]);
+    return error;
+}
+
+static int getRotation()
+{
+    jint rotation;
+
+    // Get the android application's activity.
+    ANativeActivity* activity = __state->activity;
+    JavaVM* jvm = __state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint res = jvm->AttachCurrentThread(&env, NULL);
+    if (res == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
+        return -1; 
+    }
+    GP_ASSERT(env);
+
+    jclass clsContext = env->FindClass("android/content/Context");
+    GP_ASSERT(clsContext != NULL);
+    jmethodID getSystemService = env->GetMethodID(clsContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+    GP_ASSERT(getSystemService != NULL);
+    jfieldID WINDOW_SERVICE_ID = env->GetStaticFieldID(clsContext, "WINDOW_SERVICE", "Ljava/lang/String;");
+    GP_ASSERT(WINDOW_SERVICE_ID != NULL);
+    jstring WINDOW_SERVICE = (jstring) env->GetStaticObjectField(clsContext, WINDOW_SERVICE_ID);
+    GP_ASSERT(WINDOW_SERVICE != NULL);
+    jobject windowManager = env->CallObjectMethod(activity->clazz, getSystemService, WINDOW_SERVICE);
+    GP_ASSERT(windowManager != NULL);
+    jclass clsWindowManager = env->FindClass("android/view/WindowManager");
+    GP_ASSERT(clsWindowManager != NULL);
+    jmethodID getDefaultDisplay = env->GetMethodID(clsWindowManager, "getDefaultDisplay", "()Landroid/view/Display;");
+    GP_ASSERT(getDefaultDisplay != NULL);
+    jobject defaultDisplay = env->CallObjectMethod(windowManager, getDefaultDisplay);
+    GP_ASSERT(defaultDisplay != NULL);
+    jclass clsDisplay = env->FindClass("android/view/Display");
+    GP_ASSERT(clsDisplay != NULL);
+    jmethodID getRotation = env->GetMethodID(clsDisplay, "getRotation", "()I");
+    GP_ASSERT(getRotation != NULL)
+    rotation =  env->CallIntMethod(defaultDisplay, getRotation);
+
+    return rotation;
+}
+
+
+// Initialized EGL resources.
+static bool initEGL()
+{
+    int samples = 0;
+    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
+    if (config)
+    {
+        samples = std::max(config->getInt("samples"), 0);
+    }
+
+    // Hard-coded to 32-bit/OpenGL ES 2.0.
+    // NOTE: EGL_SAMPLE_BUFFERS, EGL_SAMPLES and EGL_DEPTH_SIZE MUST remain at the beginning of the attribute list
+    // since they are expected to be at indices 0-5 in config fallback code later.
+    // EGL_DEPTH_SIZE is also expected to
+    EGLint eglConfigAttrs[] =
+    {
+        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
+        EGL_SAMPLES,            samples,
+        EGL_DEPTH_SIZE,         24,
+        EGL_RED_SIZE,           8,
+        EGL_GREEN_SIZE,         8,
+        EGL_BLUE_SIZE,          8,
+        EGL_ALPHA_SIZE,         8,
+        EGL_STENCIL_SIZE,       8,
+        EGL_SURFACE_TYPE,       EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
+        EGL_NONE
+    };
+    __multiSampling = samples > 0;
+    
+    EGLint eglConfigCount;
+    const EGLint eglContextAttrs[] =
+    {
+        EGL_CONTEXT_CLIENT_VERSION,    2,
+        EGL_NONE
+    };
+
+    const EGLint eglSurfaceAttrs[] =
+    {
+        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
+        EGL_NONE
+    };
+
+    if (__eglDisplay == EGL_NO_DISPLAY && __eglContext == EGL_NO_CONTEXT)
+    {
+        // 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;
+        }
+
+        // Try both 24 and 16-bit depth sizes since some hardware (i.e. Tegra) does not support 24-bit depth
+        bool validConfig = false;
+        EGLint depthSizes[] = { 24, 16 };
+        for (unsigned int i = 0; i < 2; ++i)
+        {
+            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
+            eglConfigAttrs[3] = samples;
+            eglConfigAttrs[5] = depthSizes[i];
+
+            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+            {
+                validConfig = true;
+                break;
+            }
+
+            if (samples)
+            {
+                // Try lowering the MSAA sample size until we find a supported config
+                int sampleCount = samples;
+                while (sampleCount)
+                {
+                    GP_WARN("No EGL config found for depth_size=%d and samples=%d. Trying samples=%d instead.", depthSizes[i], sampleCount, sampleCount / 2);
+                    sampleCount /= 2;
+                    eglConfigAttrs[1] = sampleCount > 0 ? 1 : 0;
+                    eglConfigAttrs[3] = sampleCount;
+                    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+                    {
+                        validConfig = true;
+                        break;
+                    }
+                }
+
+                __multiSampling = sampleCount > 0;
+
+                if (validConfig)
+                    break;
+            }
+            else
+            {
+                GP_WARN("No EGL config found for depth_size=%d.", depthSizes[i]);
+            }
+        }
+
+        if (!validConfig)
+        {
+            checkErrorEGL("eglChooseConfig");
+            goto error;
+        }
+
+        __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
+        if (__eglContext == EGL_NO_CONTEXT)
+        {
+            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);
+
+    __orientationAngle = getRotation() * 90;
+    
+    // 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;
+}
+
+static void destroyEGLSurface()
+{
+    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;
+    }
+}
+
+static void destroyEGLMain()
+{
+    destroyEGLSurface();
+
+    if (__eglContext != EGL_NO_CONTEXT)
+    {
+        eglDestroyContext(__eglDisplay, __eglContext);
+        __eglContext = EGL_NO_CONTEXT;
+    }
+
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglTerminate(__eglDisplay);
+        __eglDisplay = EGL_NO_DISPLAY;
+    }
+}
+
+// Display the android virtual keyboard.
+static void displayKeyboard(android_app* state, bool show)
+{ 
+    // 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);
+    
+    GP_ASSERT(state && state->activity && state->activity->vm);
+
+    // Show or hide the keyboard by calling the appropriate Java method through JNI instead.
+    jint flags = 0;
+    JavaVM* jvm = state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint result = jvm->AttachCurrentThread(&env, NULL);
+    if (result == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
+        return; 
+    }
+    GP_ASSERT(env);
+
+    // Retrieves NativeActivity. 
+    jobject lNativeActivity = state->activity->clazz;
+    jclass ClassNativeActivity = env->GetObjectClass(lNativeActivity);
+
+    // Retrieves Context.INPUT_METHOD_SERVICE.
+    jclass ClassContext = env->FindClass("android/content/Context");
+    jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID(ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
+    jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField(ClassContext, FieldINPUT_METHOD_SERVICE);
+    
+    // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
+    jclass ClassInputMethodManager = env->FindClass("android/view/inputmethod/InputMethodManager");
+    jmethodID MethodGetSystemService = env->GetMethodID(ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+    jobject lInputMethodManager = env->CallObjectMethod(lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
+    
+    // Runs getWindow().getDecorView().
+    jmethodID MethodGetWindow = env->GetMethodID(ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
+    jobject lWindow = env->CallObjectMethod(lNativeActivity, MethodGetWindow);
+    jclass ClassWindow = env->FindClass("android/view/Window");
+    jmethodID MethodGetDecorView = env->GetMethodID(ClassWindow, "getDecorView", "()Landroid/view/View;");
+    jobject lDecorView = env->CallObjectMethod(lWindow, MethodGetDecorView);
+    if (show)
+    {
+        // Runs lInputMethodManager.showSoftInput(...).
+        jmethodID MethodShowSoftInput = env->GetMethodID( ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
+        jboolean result = env->CallBooleanMethod(lInputMethodManager, MethodShowSoftInput, lDecorView, flags); 
+    } 
+    else 
+    { 
+        // Runs lWindow.getViewToken() 
+        jclass ClassView = env->FindClass("android/view/View");
+        jmethodID MethodGetWindowToken = env->GetMethodID(ClassView, "getWindowToken", "()Landroid/os/IBinder;");
+        jobject lBinder = env->CallObjectMethod(lDecorView, MethodGetWindowToken); 
+        
+        // lInputMethodManager.hideSoftInput(...). 
+        jmethodID MethodHideSoftInput = env->GetMethodID(ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); 
+        jboolean lRes = env->CallBooleanMethod( lInputMethodManager, MethodHideSoftInput, lBinder, flags); 
+    }
+    
+    // Finished with the JVM.
+    jvm->DetachCurrentThread(); 
+}
+
+// Gets the Keyboard::Key enumeration constant that corresponds to the given Android key code.
+static Keyboard::Key getKey(int keycode, int metastate)
+{
+    bool shiftOn = (metastate == AMETA_SHIFT_ON);
+    
+    switch(keycode)
+    {
+        case AKEYCODE_HOME:
+            return Keyboard::KEY_HOME;
+        case AKEYCODE_0:
+            return Keyboard::KEY_ZERO;
+        case AKEYCODE_1:
+            return Keyboard::KEY_ONE;
+        case AKEYCODE_2:
+            return Keyboard::KEY_TWO;
+        case AKEYCODE_3:
+            return Keyboard::KEY_THREE;
+        case AKEYCODE_4:
+            return Keyboard::KEY_FOUR;
+        case AKEYCODE_5:
+            return Keyboard::KEY_FIVE;
+        case AKEYCODE_6:
+            return Keyboard::KEY_SIX;
+        case AKEYCODE_7:
+            return Keyboard::KEY_SEVEN;
+        case AKEYCODE_8:
+            return Keyboard::KEY_EIGHT;
+        case AKEYCODE_9:
+            return Keyboard::KEY_NINE;
+        case AKEYCODE_STAR:
+            return Keyboard::KEY_ASTERISK;
+        case AKEYCODE_POUND:
+            return Keyboard::KEY_NUMBER;
+        case AKEYCODE_DPAD_UP:
+            return Keyboard::KEY_UP_ARROW;
+        case AKEYCODE_DPAD_DOWN:
+            return Keyboard::KEY_DOWN_ARROW;
+        case AKEYCODE_DPAD_LEFT:
+            return Keyboard::KEY_LEFT_ARROW;
+        case AKEYCODE_DPAD_RIGHT:
+            return Keyboard::KEY_RIGHT_ARROW;
+        case AKEYCODE_A:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
+        case AKEYCODE_B:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
+       case AKEYCODE_C:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
+        case AKEYCODE_D:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
+        case AKEYCODE_E:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
+        case AKEYCODE_F:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
+        case AKEYCODE_G:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
+        case AKEYCODE_H:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
+        case AKEYCODE_I:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
+        case AKEYCODE_J:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
+        case AKEYCODE_K:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
+        case AKEYCODE_L:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
+        case AKEYCODE_M:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
+        case AKEYCODE_N:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
+        case AKEYCODE_O:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
+        case AKEYCODE_P:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
+        case AKEYCODE_Q:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
+        case AKEYCODE_R:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
+        case AKEYCODE_S:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
+        case AKEYCODE_T:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
+        case AKEYCODE_U:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
+        case AKEYCODE_V:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
+        case AKEYCODE_W:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
+        case AKEYCODE_X:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
+        case AKEYCODE_Y:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case AKEYCODE_Z:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case AKEYCODE_COMMA:
+            return Keyboard::KEY_COMMA;
+        case AKEYCODE_PERIOD:
+            return Keyboard::KEY_PERIOD;
+        case AKEYCODE_ALT_LEFT:
+        case AKEYCODE_ALT_RIGHT:
+            return Keyboard::KEY_ALT;
+        case AKEYCODE_SHIFT_LEFT:
+        case AKEYCODE_SHIFT_RIGHT:
+            return Keyboard::KEY_SHIFT;
+        case AKEYCODE_TAB:
+            return Keyboard::KEY_TAB;
+        case AKEYCODE_SPACE:
+            return Keyboard::KEY_SPACE;
+        case AKEYCODE_ENTER:
+            return Keyboard::KEY_RETURN;
+        case AKEYCODE_DEL:
+            return Keyboard::KEY_DELETE;
+        case AKEYCODE_GRAVE:
+            return Keyboard::KEY_GRAVE;
+        case AKEYCODE_MINUS:
+            return Keyboard::KEY_MINUS;
+        case AKEYCODE_EQUALS:
+            return Keyboard::KEY_EQUAL;
+        case AKEYCODE_LEFT_BRACKET:
+            return Keyboard::KEY_LEFT_BRACKET;
+        case AKEYCODE_RIGHT_BRACKET:
+            return Keyboard::KEY_RIGHT_BRACKET;
+        case AKEYCODE_BACKSLASH:
+            return Keyboard::KEY_BACK_SLASH;
+        case AKEYCODE_SEMICOLON:
+            return Keyboard::KEY_SEMICOLON;
+        case AKEYCODE_APOSTROPHE:
+            return Keyboard::KEY_APOSTROPHE;
+        case AKEYCODE_SLASH:
+            return Keyboard::KEY_SLASH;
+        case AKEYCODE_AT:
+            return Keyboard::KEY_AT;
+        case AKEYCODE_PLUS:
+            return Keyboard::KEY_PLUS;
+        case AKEYCODE_PAGE_UP:
+            return Keyboard::KEY_PG_UP;
+        case AKEYCODE_PAGE_DOWN:
+            return Keyboard::KEY_PG_DOWN;
+        case AKEYCODE_MENU:
+            return Keyboard::KEY_MENU;
+        case AKEYCODE_SEARCH:
+            return Keyboard::KEY_SEARCH;
+        default:
+            return Keyboard::KEY_NONE;
+    }
+}
+
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+static int getUnicode(int keycode, int metastate)
+{
+    if (keycode == AKEYCODE_DEL)
+        return 0x0008;
+    // TODO: Doesn't support unicode currently.
+    Keyboard::Key key = getKey(keycode, metastate);
+    switch (key)
+    {
+    case Keyboard::KEY_BACKSPACE:
+        return 0x0008;
+    case Keyboard::KEY_TAB:
+        return 0x0009;
+    case Keyboard::KEY_RETURN:
+    case Keyboard::KEY_KP_ENTER:
+        return 0x000A;
+    case Keyboard::KEY_ESCAPE:
+        return 0x001B;
+    case Keyboard::KEY_SPACE:
+    case Keyboard::KEY_EXCLAM:
+    case Keyboard::KEY_QUOTE:
+    case Keyboard::KEY_NUMBER:
+    case Keyboard::KEY_DOLLAR:
+    case Keyboard::KEY_PERCENT:
+    case Keyboard::KEY_CIRCUMFLEX:
+    case Keyboard::KEY_AMPERSAND:
+    case Keyboard::KEY_APOSTROPHE:
+    case Keyboard::KEY_LEFT_PARENTHESIS:
+    case Keyboard::KEY_RIGHT_PARENTHESIS:
+    case Keyboard::KEY_ASTERISK:
+    case Keyboard::KEY_PLUS:
+    case Keyboard::KEY_COMMA:
+    case Keyboard::KEY_MINUS:
+    case Keyboard::KEY_PERIOD:
+    case Keyboard::KEY_SLASH:
+    case Keyboard::KEY_ZERO:
+    case Keyboard::KEY_ONE:
+    case Keyboard::KEY_TWO:
+    case Keyboard::KEY_THREE:
+    case Keyboard::KEY_FOUR:
+    case Keyboard::KEY_FIVE:
+    case Keyboard::KEY_SIX:
+    case Keyboard::KEY_SEVEN:
+    case Keyboard::KEY_EIGHT:
+    case Keyboard::KEY_NINE:
+    case Keyboard::KEY_COLON:
+    case Keyboard::KEY_SEMICOLON:
+    case Keyboard::KEY_LESS_THAN:
+    case Keyboard::KEY_EQUAL:
+    case Keyboard::KEY_GREATER_THAN:
+    case Keyboard::KEY_QUESTION:
+    case Keyboard::KEY_AT:
+    case Keyboard::KEY_CAPITAL_A:
+    case Keyboard::KEY_CAPITAL_B:
+    case Keyboard::KEY_CAPITAL_C:
+    case Keyboard::KEY_CAPITAL_D:
+    case Keyboard::KEY_CAPITAL_E:
+    case Keyboard::KEY_CAPITAL_F:
+    case Keyboard::KEY_CAPITAL_G:
+    case Keyboard::KEY_CAPITAL_H:
+    case Keyboard::KEY_CAPITAL_I:
+    case Keyboard::KEY_CAPITAL_J:
+    case Keyboard::KEY_CAPITAL_K:
+    case Keyboard::KEY_CAPITAL_L:
+    case Keyboard::KEY_CAPITAL_M:
+    case Keyboard::KEY_CAPITAL_N:
+    case Keyboard::KEY_CAPITAL_O:
+    case Keyboard::KEY_CAPITAL_P:
+    case Keyboard::KEY_CAPITAL_Q:
+    case Keyboard::KEY_CAPITAL_R:
+    case Keyboard::KEY_CAPITAL_S:
+    case Keyboard::KEY_CAPITAL_T:
+    case Keyboard::KEY_CAPITAL_U:
+    case Keyboard::KEY_CAPITAL_V:
+    case Keyboard::KEY_CAPITAL_W:
+    case Keyboard::KEY_CAPITAL_X:
+    case Keyboard::KEY_CAPITAL_Y:
+    case Keyboard::KEY_CAPITAL_Z:
+    case Keyboard::KEY_LEFT_BRACKET:
+    case Keyboard::KEY_BACK_SLASH:
+    case Keyboard::KEY_RIGHT_BRACKET:
+    case Keyboard::KEY_UNDERSCORE:
+    case Keyboard::KEY_GRAVE:
+    case Keyboard::KEY_A:
+    case Keyboard::KEY_B:
+    case Keyboard::KEY_C:
+    case Keyboard::KEY_D:
+    case Keyboard::KEY_E:
+    case Keyboard::KEY_F:
+    case Keyboard::KEY_G:
+    case Keyboard::KEY_H:
+    case Keyboard::KEY_I:
+    case Keyboard::KEY_J:
+    case Keyboard::KEY_K:
+    case Keyboard::KEY_L:
+    case Keyboard::KEY_M:
+    case Keyboard::KEY_N:
+    case Keyboard::KEY_O:
+    case Keyboard::KEY_P:
+    case Keyboard::KEY_Q:
+    case Keyboard::KEY_R:
+    case Keyboard::KEY_S:
+    case Keyboard::KEY_T:
+    case Keyboard::KEY_U:
+    case Keyboard::KEY_V:
+    case Keyboard::KEY_W:
+    case Keyboard::KEY_X:
+    case Keyboard::KEY_Y:
+    case Keyboard::KEY_Z:
+    case Keyboard::KEY_LEFT_BRACE:
+    case Keyboard::KEY_BAR:
+    case Keyboard::KEY_RIGHT_BRACE:
+    case Keyboard::KEY_TILDE:
+        return key;
+    default:
+        return 0;
+    }
+}
+
+// Process the next input event.
+static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
+{
+    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
+    {
+        int32_t action = AMotionEvent_getAction(event);
+        size_t pointerIndex;
+        size_t pointerId;
+        size_t pointerCount;
+        int x;
+        int y;
+        
+        switch (action & AMOTION_EVENT_ACTION_MASK)
+        {
+            case AMOTION_EVENT_ACTION_DOWN:
+                {
+                    pointerId = AMotionEvent_getPointerId(event, 0);
+                    x = AMotionEvent_getX(event, 0);
+                    y = AMotionEvent_getY(event, 0);
+
+                    // Gesture handling
+                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+                    {
+                        __pointer0.pressed = true;
+                        __pointer0.time = Game::getInstance()->getAbsoluteTime();
+                        __pointer0.pointerId = pointerId;
+                        __pointer0.x = x;
+                        __pointer0.y = y;
+                    }
+
+                    // Primary pointer down.
+                    gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, x, y, pointerId);
+                    __primaryTouchId = pointerId;
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_UP:
+                {
+                    pointerId = AMotionEvent_getPointerId(event, 0);
+                    x = AMotionEvent_getX(event, 0);
+                    y = AMotionEvent_getY(event, 0);
+                    
+                    // Gestures
+                    bool gestureDetected = false;
+                    if ( __pointer0.pressed &&  __pointer0.pointerId == pointerId)
+                    {
+                        int deltaX = x - __pointer0.x;
+                        int deltaY = y - __pointer0.y;
+
+                        // Test for swipe
+                        if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
+                            gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_SWIPE_DURATION_MAX && 
+                            (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
+                        {
+                            int direction = 0;
+                            if ( abs(deltaX) > abs(deltaY) )
+                            {
+                                if (deltaX > 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
+                                else if (deltaX < 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_LEFT;
+                            }
+                            else
+                            {
+                                if (deltaY > 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_DOWN;
+                                else if (deltaY < 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_UP;
+                            }
+                            gameplay::Game::getInstance()->gestureSwipeEvent(x, y, direction);
+                            __pointer0.pressed = false;
+                            gestureDetected = true;
+                        }
+                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
+                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_TAP_DURATION_MAX)
+                        {
+                            gameplay::Game::getInstance()->gestureTapEvent(x, y);
+                            __pointer0.pressed = false;
+                            gestureDetected = true;
+                        }
+                    }
+
+                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
+                    {
+                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, x, y, pointerId);
+                    }
+                    __primaryTouchId = -1;
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_POINTER_DOWN:
+                {
+                    pointerId = AMotionEvent_getPointerId(event, 0);
+                    x = AMotionEvent_getX(event, 0);
+                    y = AMotionEvent_getY(event, 0);
+
+                    // Gesture handling
+                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+                    {
+                        __pointer1.pressed = true;
+                        __pointer1.time = Game::getInstance()->getAbsoluteTime();
+                        __pointer1.pointerId = pointerId;
+                        __pointer1.x = x;
+                        __pointer1.y = y;
+                    }
+
+                    // Non-primary pointer down.
+                    if (__multiTouch)
+                    {
+                        pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+                        pointerId = AMotionEvent_getPointerId(event, pointerIndex);
+                        gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, AMotionEvent_getX(event, pointerIndex), AMotionEvent_getY(event, pointerIndex), pointerId);
+                    }
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_POINTER_UP:
+                {
+                    pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+                    pointerId = AMotionEvent_getPointerId(event, pointerIndex);
+                    x = AMotionEvent_getX(event, 0);
+                    y = AMotionEvent_getY(event, 0);
+
+                    bool gestureDetected = false;
+                    if ( __pointer1.pressed &&  __pointer1.pointerId == pointerId)
+                    {
+                        int deltaX = x - __pointer1.x;
+                        int deltaY = y - __pointer1.y;
+
+                        // Test for swipe
+                        if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
+                            gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_SWIPE_DURATION_MAX && 
+                            (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
+                        {
+                            int direction;
+                            if (deltaX > 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
+                            else if (deltaX < 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_LEFT;
+                            
+                            if (deltaY > 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_DOWN;
+                            else if (deltaY < 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_UP;
+
+                            gameplay::Game::getInstance()->gestureSwipeEvent(x, y, direction);
+                            __pointer1.pressed = false;
+                            gestureDetected = true;
+                        }
+                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
+                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_TAP_DURATION_MAX)
+                        {
+                            gameplay::Game::getInstance()->gestureTapEvent(x, y);
+                            __pointer1.pressed = false;
+                            gestureDetected = true;
+                        }
+                    }
+
+                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
+                    {
+                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, AMotionEvent_getX(event, pointerIndex), AMotionEvent_getY(event, pointerIndex), pointerId);
+                    }
+                    if (__primaryTouchId == pointerId)
+                        __primaryTouchId = -1;
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_MOVE:
+                {
+                    // ACTION_MOVE events are batched, unlike the other events.
+                    pointerCount = AMotionEvent_getPointerCount(event);
+                    for (size_t i = 0; i < pointerCount; ++i)
+                    {
+                        pointerId = AMotionEvent_getPointerId(event, i);
+                        if (__multiTouch || __primaryTouchId == pointerId)
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, AMotionEvent_getX(event, i), AMotionEvent_getY(event, i), pointerId);
+                        }
+                    }
+                }
+                break;
+        }
+        return 1;
+    } 
+    else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
+    {
+        int32_t action = AKeyEvent_getAction(event);
+        int32_t keycode = AKeyEvent_getKeyCode(event);
+        int32_t metastate = AKeyEvent_getMetaState(event); 
+        
+        switch(action)
+        {
+            case AKEY_EVENT_ACTION_DOWN:
+                Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, getKey(keycode, metastate));
+                if (int character = getUnicode(keycode, metastate))
+                    gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
+                break;
+                    
+            case AKEY_EVENT_ACTION_UP:
+                gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
+                break;
+        }
+    }
+    return 0;
+}
+
+// Process the next main command.
+static void engine_handle_cmd(struct android_app* app, int32_t cmd)
+{
+    switch (cmd) 
+    {
+        case APP_CMD_INIT_WINDOW:
+            // The window is being shown, get it ready.
+            if (app->window != NULL)
+            {
+                initEGL();
+                __initialized = true;
+            }
+            break;
+        case APP_CMD_TERM_WINDOW:
+            destroyEGLSurface();
+            __initialized = false;
+            break;
+        case APP_CMD_DESTROY:
+            Game::getInstance()->exit();
+            destroyEGLMain();
+            __initialized = false;
+            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 microseconds).
+                ASensorEventQueue_setEventRate(__sensorEventQueue, __accelerometerSensor, (1000L/60)*1000);
+            }
+
+            if (Game::getInstance()->getState() == Game::UNINITIALIZED)
+            {
+                Game::getInstance()->run();
+            }
+            else
+            {
+                Game::getInstance()->resume();
+            }
+            break;
+        case APP_CMD_RESUME:
+            if (__initialized)
+            {
+                Game::getInstance()->resume();
+            }
+            __suspended = false;
+            break;
+        case APP_CMD_PAUSE:
+            Game::getInstance()->pause();
+            __suspended = true;
+            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);
+            }
+            break;
+    }
+}
+
+Platform::Platform(Game* game)
+    : _game(game)
+{
+}
+
+Platform::~Platform()
+{
+}
+
+Platform* Platform::create(Game* game, void* attachToWindow)
+{
+    Platform* platform = new Platform(game);
+    return platform;
+}
+
+int Platform::enterMessagePump()
+{
+    GP_ASSERT(__state && __state->activity && __state->activity->vm);
+
+    __initialized = false;
+    __suspended = false;
+
+    // Get the android application's activity.
+    ANativeActivity* activity = __state->activity;
+    JavaVM* jvm = __state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint res = jvm->AttachCurrentThread(&env, NULL);
+    if (res == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
+        return -1; 
+    }
+    GP_ASSERT(env);
+
+    /* Get external files directory on Android; this will result in a directory where all app files
+     * should be stored, like /mnt/sdcard/android/<package-name>/files/
+     */
+    jboolean isCopy;
+
+    jclass clazz = env->GetObjectClass(activity->clazz);
+    jmethodID methodGetExternalStorage = env->GetMethodID(clazz, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
+
+    jclass clazzFile = env->FindClass("java/io/File");
+    jmethodID methodGetPath = env->GetMethodID(clazzFile, "getPath", "()Ljava/lang/String;");
+
+    // Now has java.io.File object pointing to directory
+    jobject objectFile  = env->CallObjectMethod(activity->clazz, methodGetExternalStorage, NULL);
+    
+    // Now has String object containing path to directory
+    jstring stringExternalPath = static_cast<jstring>(env->CallObjectMethod(objectFile, methodGetPath));
+    const char* externalPath = env->GetStringUTFChars(stringExternalPath, &isCopy);
+
+    // Set the default path to store the resources.
+    std::string assetsPath(externalPath);
+    if (externalPath[strlen(externalPath)-1] != '/')
+        assetsPath += "/";
+
+    FileSystem::setResourcePath(assetsPath.c_str());    
+
+    // Release string data
+    env->ReleaseStringUTFChars(stringExternalPath, externalPath);
+    jvm->DetachCurrentThread();
+    
+    // Get the asset manager to get the resources from the .apk file.
+    __assetManager = 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;
+    
+    while (true)
+    {
+        // Read all pending events.
+        int ident;
+        int events;
+        struct android_poll_source* source;
+        
+        while ((ident=ALooper_pollAll(!__suspended ? 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)
+            {
+                return 0;
+            }
+        }
+        
+        // Idle time (no events left to process) is spent rendering.
+        // We skip rendering when the app is paused.
+        if (__initialized && !__suspended)
+        {
+            _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)
+            {
+                EGLint error = eglGetError();
+                if (error == EGL_BAD_NATIVE_WINDOW)
+                {
+                    if (__state->window != NULL)
+                    {
+                        destroyEGLSurface();
+                        initEGL();
+                    }
+                    __initialized = true;
+                }
+                else
+                {
+                    perror("eglSwapBuffers");
+                    break;
+                }
+            }
+        }
+            
+        // Display the keyboard.
+        gameplay::displayKeyboard(__state, __displayKeyboard);
+    }
+}
+
+void Platform::signalShutdown() 
+{
+    // nothing to do  
+}
+
+bool Platform::canExit()
+{
+    return true;
+}
+   
+unsigned int Platform::getDisplayWidth()
+{
+    return __width;
+}
+    
+unsigned int Platform::getDisplayHeight()
+{
+    return __height;
+}
+    
+double Platform::getAbsoluteTime()
+{
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    double now = timespec2millis(&__timespec);
+    __timeAbsolute = now - __timeStart;
+
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(double time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
+    __vsync = enable;
+}
+
+
+void Platform::swapBuffers()
+{
+    if (__eglDisplay && __eglSurface)
+        eglSwapBuffers(__eglDisplay, __eglSurface);
+}
+
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
+void Platform::setMultiSampling(bool enabled)
+{
+    if (enabled == __multiSampling)
+    {
+        return;
+    }
+
+    //todo
+
+    __multiSampling = enabled;
+}
+
+bool Platform::isMultiSampling()
+{
+    return __multiSampling;
+}
+
+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.
+    switch (__orientationAngle)
+    {
+    case 90:
+        tx = -__sensorEvent.acceleration.y;
+        ty = __sensorEvent.acceleration.x;
+        break;
+    case 180:
+        tx = -__sensorEvent.acceleration.x;
+        ty = -__sensorEvent.acceleration.y;
+        break;
+    case 270:
+        tx = __sensorEvent.acceleration.y;
+        ty = -__sensorEvent.acceleration.x;
+        break;
+    default:
+        tx = __sensorEvent.acceleration.x;
+        ty = __sensorEvent.acceleration.y;
+        break;
+    }
+    tz = __sensorEvent.acceleration.z;
+
+    if (pitch != NULL)
+    {
+        GP_ASSERT(tx * tx + tz * tz);
+        *pitch = -atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
+    }
+    if (roll != NULL)
+    {
+        GP_ASSERT(ty * ty + tz * tz);
+        *roll = -atan(tx / sqrt(ty * ty + tz * tz)) * 180.0f * M_1_PI;
+    }
+}
+
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = 0;
+    if (argv)
+        *argv = 0;
+}
+
+bool Platform::hasMouse()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setMouseCaptured(bool captured)
+{
+    // not supported
+}
+
+bool Platform::isMouseCaptured()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setCursorVisible(bool visible)
+{
+    // not supported
+}
+
+bool Platform::isCursorVisible()
+{
+    // not supported
+    return false;
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    if (display)
+        __displayKeyboard = true;
+    else
+        __displayKeyboard = false;
+}
+
+void Platform::shutdownInternal()
+{
+    Game::getInstance()->shutdown();
+}
+
+bool Platform::isGestureSupported(Gesture::GestureEvent evt)
+{
+    // Pinch currently not implemented
+    return evt == gameplay::Gesture::GESTURE_SWIPE || evt == gameplay::Gesture::GESTURE_TAP;
+}
+
+void Platform::registerGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.set();
+        break;
+
+    case Gesture::GESTURE_TAP:
+    case Gesture::GESTURE_SWIPE:
+        __gestureEventsProcessed.set(evt);
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Platform::unregisterGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.reset();
+        break;
+
+    case Gesture::GESTURE_TAP:
+    case Gesture::GESTURE_SWIPE:
+        __gestureEventsProcessed.set(evt, 0);
+        break;
+
+    default:
+        break;
+    }
+}
+    
+bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
+{
+    return __gestureEventsProcessed.test(evt);
+}
+
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+}
+
+bool Platform::launchURL(const char *url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    bool result = true;
+
+    android_app* state = __state;
+    GP_ASSERT(state && state->activity && state->activity->vm);
+    JavaVM* jvm = state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint r = jvm->AttachCurrentThread(&env, NULL);
+    if (r == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
+        return false;
+    }
+    GP_ASSERT(env);
+
+    jclass classActivity = env->FindClass("android/app/NativeActivity");
+    jclass classIntent = env->FindClass("android/content/Intent");
+    jclass classUri = env->FindClass("android/net/Uri");
+
+    GP_ASSERT(classActivity && classIntent && classUri);
+
+    // Get static field ID Intent.ACTION_VIEW
+    jfieldID fieldActionView = env->GetStaticFieldID(classIntent, "ACTION_VIEW", "Ljava/lang/String;");
+    GP_ASSERT(fieldActionView);
+
+    // Get string value of Intent.ACTION_VIEW, we'll need that to pass to Intent's constructor later on
+    jstring paramActionView = (jstring)env->GetStaticObjectField(classIntent, fieldActionView);
+    GP_ASSERT(paramActionView);
+
+    // Get method ID Uri.parse, will be needed to parse the url given into Uri object
+    jmethodID methodUriParse = env->GetStaticMethodID(classUri, "parse","(Ljava/lang/String;)Landroid/net/Uri;");
+    GP_ASSERT(methodUriParse);
+
+    // Get method ID Activity.startActivity, so we can start the appropriate activity for the View action of our Uri
+    jmethodID methodActivityStartActivity = env->GetMethodID(classActivity, "startActivity","(Landroid/content/Intent;)V");
+    GP_ASSERT(methodActivityStartActivity);
+
+    // Get method ID Intent constructor, the one that takes action and uri (String;Uri)
+    jmethodID methodIntentInit = env->GetMethodID(classIntent, "<init>","(Ljava/lang/String;Landroid/net/Uri;)V");
+    GP_ASSERT(methodIntentInit);
+
+    // Convert our url to Java's string and parse it to Uri
+    jstring paramUrlString = env->NewStringUTF(url);
+    jobject paramUri = env->CallStaticObjectMethod(classUri, methodUriParse, paramUrlString);
+    GP_ASSERT(paramUri);
+
+    // Create Intent with Intent.ACTION_VIEW and parsed Uri arguments
+    jobject paramIntent = env->NewObject(classIntent, methodIntentInit, paramActionView, paramUri);
+    GP_ASSERT(paramIntent);
+
+    // Launch NativeActivity.startActivity with our intent to view the url! state->activity->clazz holds
+    // our NativeActivity object
+    env->CallVoidMethod(state->activity->clazz, methodActivityStartActivity, paramIntent);
+
+    /* startActivity may throw a ActivitNotFoundException if, well, activity is not found.
+       Example: http://<url> is passed to the intent but there is no browser installed in the system
+       we need to handle it. */
+    jobject exception = env->ExceptionOccurred();
+
+    // We're not lucky here
+    if (exception)
+    {
+        // Print out the exception data to logcat
+        env->ExceptionDescribe();
+
+        // Exception needs to be cleared
+        env->ExceptionClear();
+
+        // Launching the url failed
+        result = false;
+    }
+
+    // See you Space Cowboy
+    jvm->DetachCurrentThread();
+    return result;
+}
+
+}
+
+#endif

+ 1587 - 1599
gameplay/src/PlatformBlackBerry.cpp

@@ -1,1599 +1,1587 @@
-#ifdef __QNX__
-
-#include "Base.h"
-#include "Platform.h"
-#include "FileSystem.h"
-#include "Game.h"
-#include "Form.h"
-#include "ScriptController.h"
-#include <unistd.h>
-#include <sys/keycodes.h>
-#include <screen/screen.h>
-#include <input/screen_helpers.h>
-#include <gestures/set.h>
-#include <gestures/swipe.h>
-#include <gestures/pinch.h>
-#include <gestures/tap.h>
-#include <bps/bps.h>
-#include <bps/event.h>
-#include <bps/screen.h>
-#include <bps/navigator.h>
-#include <bps/sensor.h>
-#include <bps/orientation.h>
-#include <bps/virtualkeyboard.h>
-
-#define TOUCH_COUNT_MAX     4
-
-using namespace std;
-
-struct timespec __timespec;
-static double __timeStart;
-static double __timeAbsolute;
-static bool __vsync = WINDOW_VSYNC;
-static screen_context_t __screenContext;
-static screen_window_t __screenWindow;
-static screen_event_t __screenEvent;
-static int __screenWindowSize[2];
-static bool __screenFullscreen = false;
-static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
-static EGLContext __eglContext = EGL_NO_CONTEXT;
-static EGLSurface __eglSurface = EGL_NO_SURFACE;
-static EGLConfig __eglConfig = 0;
-static int __orientationAngle;
-static bool __multiTouch = false;
-static float __pitch;
-static float __roll;
-static const char* __glExtensions;
-static struct gestures_set * __gestureSet;
-static bitset<3> __gestureEventsProcessed;
-static bool __gestureSwipeRecognized = false;
-PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
-PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
-PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
-PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
-
-namespace gameplay
-{
-
-// Gets the Keyboard::Key enumeration constant that corresponds to the given QNX key code.
-static Keyboard::Key getKey(int qnxKeycode)
-{
-    switch (qnxKeycode)
-    {
-    case KEYCODE_SYSREQ:
-        return Keyboard::KEY_SYSREQ;
-    case KEYCODE_BREAK:
-        return Keyboard::KEY_BREAK;
-    case KEYCODE_MENU:
-        return Keyboard::KEY_MENU;
-    case KEYCODE_KP_ENTER:
-        return Keyboard::KEY_KP_ENTER;
-    case KEYCODE_PAUSE:
-        return Keyboard::KEY_PAUSE;
-    case KEYCODE_SCROLL_LOCK:
-        return Keyboard::KEY_SCROLL_LOCK;
-    case KEYCODE_PRINT:
-        return Keyboard::KEY_PRINT;
-    case KEYCODE_ESCAPE:
-        return Keyboard::KEY_ESCAPE;
-    case KEYCODE_BACKSPACE:
-        return Keyboard::KEY_BACKSPACE;
-    case KEYCODE_BACK_TAB:
-        return Keyboard::KEY_BACK_TAB;
-    case KEYCODE_TAB:
-        return Keyboard::KEY_TAB;
-    case KEYCODE_RETURN:
-        return Keyboard::KEY_RETURN;
-    case KEYCODE_CAPS_LOCK:
-        return Keyboard::KEY_CAPS_LOCK;
-    case KEYCODE_LEFT_SHIFT:
-    case KEYCODE_RIGHT_SHIFT:
-        return Keyboard::KEY_SHIFT;
-    case KEYCODE_LEFT_CTRL:
-    case KEYCODE_RIGHT_CTRL:
-        return Keyboard::KEY_CTRL;
-    case KEYCODE_LEFT_ALT:
-    case KEYCODE_RIGHT_ALT:
-        return Keyboard::KEY_ALT;
-    case KEYCODE_LEFT_HYPER:
-    case KEYCODE_RIGHT_HYPER:
-        return Keyboard::KEY_HYPER;
-    case KEYCODE_INSERT:
-        return Keyboard::KEY_INSERT;
-    case KEYCODE_HOME:
-        return Keyboard::KEY_HOME;
-    case KEYCODE_PG_UP:
-        return Keyboard::KEY_PG_UP;
-    case KEYCODE_DELETE:
-        return Keyboard::KEY_DELETE;
-    case KEYCODE_END:
-        return Keyboard::KEY_END;
-    case KEYCODE_PG_DOWN:
-        return Keyboard::KEY_PG_DOWN;
-    case KEYCODE_LEFT:
-        return Keyboard::KEY_LEFT_ARROW;
-    case KEYCODE_RIGHT:
-        return Keyboard::KEY_RIGHT_ARROW;
-    case KEYCODE_UP:
-        return Keyboard::KEY_UP_ARROW;
-    case KEYCODE_DOWN:
-        return Keyboard::KEY_DOWN_ARROW;
-    case KEYCODE_NUM_LOCK:
-        return Keyboard::KEY_NUM_LOCK;
-    case KEYCODE_KP_PLUS:
-        return Keyboard::KEY_KP_PLUS;
-    case KEYCODE_KP_MINUS:
-        return Keyboard::KEY_KP_MINUS;
-    case KEYCODE_KP_MULTIPLY:
-        return Keyboard::KEY_KP_MULTIPLY;
-    case KEYCODE_KP_DIVIDE:
-        return Keyboard::KEY_KP_DIVIDE;
-    case KEYCODE_KP_HOME:
-        return Keyboard::KEY_KP_HOME;
-    case KEYCODE_KP_UP:
-        return Keyboard::KEY_KP_UP;
-    case KEYCODE_KP_PG_UP:
-        return Keyboard::KEY_KP_PG_UP;
-    case KEYCODE_KP_LEFT:
-        return Keyboard::KEY_KP_LEFT;
-    case KEYCODE_KP_FIVE:
-        return Keyboard::KEY_KP_FIVE;
-    case KEYCODE_KP_RIGHT:
-        return Keyboard::KEY_KP_RIGHT;
-    case KEYCODE_KP_END:
-        return Keyboard::KEY_KP_END;
-    case KEYCODE_KP_DOWN:
-        return Keyboard::KEY_KP_DOWN;
-    case KEYCODE_KP_PG_DOWN:
-        return Keyboard::KEY_KP_PG_DOWN;
-    case KEYCODE_KP_INSERT:
-        return Keyboard::KEY_KP_INSERT;
-    case KEYCODE_KP_DELETE:
-        return Keyboard::KEY_KP_DELETE;
-    case KEYCODE_F1:
-        return Keyboard::KEY_F1;
-    case KEYCODE_F2:
-        return Keyboard::KEY_F2;
-    case KEYCODE_F3:
-        return Keyboard::KEY_F3;
-    case KEYCODE_F4:
-        return Keyboard::KEY_F4;
-    case KEYCODE_F5:
-        return Keyboard::KEY_F5;
-    case KEYCODE_F6:
-        return Keyboard::KEY_F6;
-    case KEYCODE_F7:
-        return Keyboard::KEY_F7;
-    case KEYCODE_F8:
-        return Keyboard::KEY_F8;
-    case KEYCODE_F9:
-        return Keyboard::KEY_F9;
-    case KEYCODE_F10:
-        return Keyboard::KEY_F10;
-    case KEYCODE_F11:
-        return Keyboard::KEY_F11;
-    case KEYCODE_F12:
-        return Keyboard::KEY_F12;
-    case KEYCODE_SPACE:
-        return Keyboard::KEY_SPACE;
-    case KEYCODE_RIGHT_PAREN:
-        return Keyboard::KEY_RIGHT_PARENTHESIS;
-    case KEYCODE_ZERO:
-        return Keyboard::KEY_ZERO;
-    case KEYCODE_EXCLAM:
-        return Keyboard::KEY_EXCLAM;
-    case KEYCODE_ONE:
-        return Keyboard::KEY_ONE;
-    case KEYCODE_AT:
-        return Keyboard::KEY_AT;
-    case KEYCODE_TWO:
-        return Keyboard::KEY_TWO;
-    case KEYCODE_NUMBER:
-        return Keyboard::KEY_NUMBER;
-    case KEYCODE_THREE:
-        return Keyboard::KEY_THREE;
-    case KEYCODE_DOLLAR:
-        return Keyboard::KEY_DOLLAR;
-    case KEYCODE_FOUR:
-        return Keyboard::KEY_FOUR;
-    case KEYCODE_PERCENT:
-        return Keyboard::KEY_PERCENT;
-    case KEYCODE_FIVE:
-        return Keyboard::KEY_FIVE;
-    case KEYCODE_CIRCUMFLEX:
-        return Keyboard::KEY_CIRCUMFLEX;
-    case KEYCODE_SIX:
-        return Keyboard::KEY_SIX;
-    case KEYCODE_AMPERSAND:
-        return Keyboard::KEY_AMPERSAND;
-    case KEYCODE_SEVEN:
-        return Keyboard::KEY_SEVEN;
-    case KEYCODE_ASTERISK:
-        return Keyboard::KEY_ASTERISK;
-    case KEYCODE_EIGHT:
-        return Keyboard::KEY_EIGHT;
-    case KEYCODE_LEFT_PAREN:
-        return Keyboard::KEY_LEFT_PARENTHESIS;
-    case KEYCODE_NINE:
-        return Keyboard::KEY_NINE;
-    case KEYCODE_EQUAL:
-        return Keyboard::KEY_EQUAL;
-    case KEYCODE_PLUS:
-        return Keyboard::KEY_PLUS;
-    case KEYCODE_LESS_THAN:
-        return Keyboard::KEY_LESS_THAN;
-    case KEYCODE_COMMA:
-        return Keyboard::KEY_COMMA;
-    case KEYCODE_UNDERSCORE:
-        return Keyboard::KEY_UNDERSCORE;
-    case KEYCODE_MINUS:
-        return Keyboard::KEY_MINUS;
-    case KEYCODE_GREATER_THAN:
-        return Keyboard::KEY_GREATER_THAN;
-    case KEYCODE_PERIOD:
-        return Keyboard::KEY_PERIOD;
-    case KEYCODE_COLON:
-        return Keyboard::KEY_COLON;
-    case KEYCODE_SEMICOLON:
-        return Keyboard::KEY_SEMICOLON;
-    case KEYCODE_QUESTION:
-        return Keyboard::KEY_QUESTION;
-    case KEYCODE_SLASH:
-        return Keyboard::KEY_SLASH;
-    case KEYCODE_GRAVE:
-        return Keyboard::KEY_GRAVE;
-    case KEYCODE_TILDE:
-        return Keyboard::KEY_TILDE;
-    case KEYCODE_LEFT_BRACE:
-        return Keyboard::KEY_LEFT_BRACE;
-    case KEYCODE_LEFT_BRACKET:
-        return Keyboard::KEY_LEFT_BRACKET;
-    case KEYCODE_BAR:
-        return Keyboard::KEY_BAR;
-    case KEYCODE_BACK_SLASH:
-        return Keyboard::KEY_BACK_SLASH;
-    case KEYCODE_RIGHT_BRACE:
-        return Keyboard::KEY_RIGHT_BRACE;
-    case KEYCODE_RIGHT_BRACKET:
-        return Keyboard::KEY_RIGHT_BRACKET;
-    case KEYCODE_QUOTE:
-        return Keyboard::KEY_QUOTE;
-    case KEYCODE_APOSTROPHE:
-        return Keyboard::KEY_APOSTROPHE;
-    case 0x20AC:
-        return Keyboard::KEY_EURO;
-    case KEYCODE_POUND_SIGN:
-        return Keyboard::KEY_POUND;
-    case KEYCODE_YEN_SIGN:
-        return Keyboard::KEY_YEN;
-    case KEYCODE_MIDDLE_DOT:
-        return Keyboard::KEY_MIDDLE_DOT;
-    case KEYCODE_CAPITAL_A:
-        return Keyboard::KEY_CAPITAL_A;
-    case KEYCODE_A:
-        return Keyboard::KEY_A;
-    case KEYCODE_CAPITAL_B:
-        return Keyboard::KEY_CAPITAL_B;
-    case KEYCODE_B:
-        return Keyboard::KEY_B;
-    case KEYCODE_CAPITAL_C:
-        return Keyboard::KEY_CAPITAL_C;
-    case KEYCODE_C:
-        return Keyboard::KEY_C;
-    case KEYCODE_CAPITAL_D:
-        return Keyboard::KEY_CAPITAL_D;
-    case KEYCODE_D:
-        return Keyboard::KEY_D;
-    case KEYCODE_CAPITAL_E:
-        return Keyboard::KEY_CAPITAL_E;
-    case KEYCODE_E:
-        return Keyboard::KEY_E;
-    case KEYCODE_CAPITAL_F:
-        return Keyboard::KEY_CAPITAL_F;
-    case KEYCODE_F:
-        return Keyboard::KEY_F;
-    case KEYCODE_CAPITAL_G:
-        return Keyboard::KEY_CAPITAL_G;
-    case KEYCODE_G:
-        return Keyboard::KEY_G;
-    case KEYCODE_CAPITAL_H:
-        return Keyboard::KEY_CAPITAL_H;
-    case KEYCODE_H:
-        return Keyboard::KEY_H;
-    case KEYCODE_CAPITAL_I:
-        return Keyboard::KEY_CAPITAL_I;
-    case KEYCODE_I:
-        return Keyboard::KEY_I;
-    case KEYCODE_CAPITAL_J:
-        return Keyboard::KEY_CAPITAL_J;
-    case KEYCODE_J:
-        return Keyboard::KEY_J;
-    case KEYCODE_CAPITAL_K:
-        return Keyboard::KEY_CAPITAL_K;
-    case KEYCODE_K:
-        return Keyboard::KEY_K;
-    case KEYCODE_CAPITAL_L:
-        return Keyboard::KEY_CAPITAL_L;
-    case KEYCODE_L:
-        return Keyboard::KEY_L;
-    case KEYCODE_CAPITAL_M:
-        return Keyboard::KEY_CAPITAL_M;
-    case KEYCODE_M:
-        return Keyboard::KEY_M;
-    case KEYCODE_CAPITAL_N:
-        return Keyboard::KEY_CAPITAL_N;
-    case KEYCODE_N:
-        return Keyboard::KEY_N;
-    case KEYCODE_CAPITAL_O:
-        return Keyboard::KEY_CAPITAL_O;
-    case KEYCODE_O:
-        return Keyboard::KEY_O;
-    case KEYCODE_CAPITAL_P:
-        return Keyboard::KEY_CAPITAL_P;
-    case KEYCODE_P:
-        return Keyboard::KEY_P;
-    case KEYCODE_CAPITAL_Q:
-        return Keyboard::KEY_CAPITAL_Q;
-    case KEYCODE_Q:
-        return Keyboard::KEY_Q;
-    case KEYCODE_CAPITAL_R:
-        return Keyboard::KEY_CAPITAL_R;
-    case KEYCODE_R:
-        return Keyboard::KEY_R;
-    case KEYCODE_CAPITAL_S:
-        return Keyboard::KEY_CAPITAL_S;
-    case KEYCODE_S:
-        return Keyboard::KEY_S;
-    case KEYCODE_CAPITAL_T:
-        return Keyboard::KEY_CAPITAL_T;
-    case KEYCODE_T:
-        return Keyboard::KEY_T;
-    case KEYCODE_CAPITAL_U:
-        return Keyboard::KEY_CAPITAL_U;
-    case KEYCODE_U:
-        return Keyboard::KEY_U;
-    case KEYCODE_CAPITAL_V:
-        return Keyboard::KEY_CAPITAL_V;
-    case KEYCODE_V:
-        return Keyboard::KEY_V;
-    case KEYCODE_CAPITAL_W:
-        return Keyboard::KEY_CAPITAL_W;
-    case KEYCODE_W:
-        return Keyboard::KEY_W;
-    case KEYCODE_CAPITAL_X:
-        return Keyboard::KEY_CAPITAL_X;
-    case KEYCODE_X:
-        return Keyboard::KEY_X;
-    case KEYCODE_CAPITAL_Y:
-        return Keyboard::KEY_CAPITAL_Y;
-    case KEYCODE_Y:
-        return Keyboard::KEY_Y;
-    case KEYCODE_CAPITAL_Z:
-        return Keyboard::KEY_CAPITAL_Z;
-    case KEYCODE_Z:
-        return Keyboard::KEY_Z;
-    default:
-        return Keyboard::KEY_NONE;
-    }
-}
-
-/**
- * Returns the unicode value from the given QNX key code value.
- * Some non-printable characters also have corresponding unicode values, such as backspace.
- *
- * @param qnxKeyCode The keyboard key code.
- *
- * @return The unicode value or 0 if the keycode did not represent a unicode key.
- */
-static int getUnicode(int qnxKeyCode)
-{
-    if (qnxKeyCode >= KEYCODE_PC_KEYS && qnxKeyCode <= UNICODE_PRIVATE_USE_AREA_LAST)
-    {
-        switch (qnxKeyCode)
-        {
-        case KEYCODE_BACKSPACE:
-            return 0x0008;
-        case KEYCODE_TAB:
-            return 0x0009;
-        case KEYCODE_KP_ENTER:
-        case KEYCODE_RETURN:
-            return 0x000A;
-        case KEYCODE_ESCAPE:
-            return 0x001B;
-        // Win32 doesn't consider delete to be a key char.
-        default:
-            return 0;
-        }
-    }
-    return qnxKeyCode;
-}
-
-extern void print(const char* format, ...)
-{
-    GP_ASSERT(format);
-    va_list argptr;
-    va_start(argptr, format);
-    vfprintf(stderr, format, argptr);
-    va_end(argptr);
-}
-
-EGLenum checkErrorEGL(const char* msg)
-{
-    GP_ASSERT(msg);
-    static const char* errmsg[] =
-    {
-        "EGL function failed",
-        "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();
-    fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
-    return error;
-}
-
-void gesture_callback(gesture_base_t* gesture, mtouch_event_t* event, void* param, int async)
-{
-    switch (gesture->type)
-    {
-    case GESTURE_SWIPE:
-        {
-            if ( __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
-            {
-                gesture_swipe_t* swipe = (gesture_swipe_t*)gesture;
-                if (!__gestureSwipeRecognized)
-                {
-                    Game::getInstance()->gestureSwipeEvent(swipe->coords.x, swipe->coords.y, swipe->direction);
-                    __gestureSwipeRecognized = true;
-                }
-
-            }
-            break;
-        }
-
-    case GESTURE_PINCH:
-        {
-            if ( __gestureEventsProcessed.test(Gesture::GESTURE_PINCH) )
-            {
-                gesture_pinch_t* pinch = (gesture_pinch_t*)gesture;
-                float dist_x = (float)pinch->last_distance.x - (float)pinch->distance.x;
-                float dist_y = (float)pinch->last_distance.y - (float)pinch->distance.y;
-                float scale = sqrt( (dist_x * dist_x) + (dist_y * dist_y) );
-                Game::getInstance()->gesturePinchEvent(pinch->centroid.x, pinch->centroid.y, scale);
-            }
-            break;
-        }
-
-    case GESTURE_TAP:
-        {
-            if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) )
-            {
-                gesture_tap_t* tap = (gesture_tap_t*)gesture;
-                Game::getInstance()->gestureTapEvent(tap->touch_coords.x, tap->touch_coords.y);
-            }
-            break;
-        }
-
-    default:
-        break;
-
-    }
-}
-
-#ifdef USE_BLACKBERRY_GAMEPAD
-
-static const char* __vendorStrings[] =
-{
-    "SteelSeries",
-    "Nintendo",
-};
-
-static const char* __productStrings[] =
-{
-    "FREE",
-    "Wii Remote",
-};
-
-static const int __VIDs[] = {
-    0x1038,
-    0x057e,
-};
-
-static const int __PIDs[] = {
-    0x1412,
-    0x0306,
-};
-
-static const unsigned int __knownGamepads = 2;
-
-void queryGamepad(GamepadHandle handle, int* buttonCount, int* joystickCount, int* productId, int* vendorId, char* productString, char* vendorString)
-{
-    char id[128];
-    screen_get_device_property_iv(handle, SCREEN_PROPERTY_BUTTON_COUNT, buttonCount);
-    screen_get_device_property_cv(handle, SCREEN_PROPERTY_ID_STRING, 128, id);
-    screen_get_device_property_cv(handle, SCREEN_PROPERTY_PRODUCT, 64, productString);
-    screen_get_device_property_cv(handle, SCREEN_PROPERTY_VENDOR, 64, vendorString);
-
-    // Check for the existence of analog sticks.
-    int analogs[3];
-    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG0, analogs))
-    {
-    	++(*joystickCount);
-    }
-
-    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG1, analogs))
-    {
-    	++(*joystickCount);
-    }
-
-    // ID string format: A-BBBB-CCCC-D.D
-    // A is the device's index
-    // BBBB is the device's Vendor ID (in hexadecimal)
-    // CCCC is the device's Product ID (also in hexadecimal)
-    // D.D is the device's version number
-    char* token = strtok(id, "-");
-    token = strtok(NULL, "-");
-    if (token)
-    {
-	    *vendorId = strtol(token, NULL, 16);
-    }
-
-    token = strtok(NULL, "-");
-    if (token)
-    {
-        *productId = strtol(token, NULL, 16);
-    }
-
-    // For gamepads unknown to BB10,
-    // check VID and PID against gamepads known to gameplay.
-    if (strlen(productString) == 0 || strlen(vendorString) == 0)
-    {
-        for (unsigned int i = 0; i < __knownGamepads; ++i)
-        {
-            if (__VIDs[i] == *vendorId && __PIDs[i] == *productId)
-            {
-            	strcpy(vendorString, __vendorStrings[i]);
-                strcpy(productString, __productStrings[i]);
-            }
-        }
-    }
-}
-
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-    screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_BUTTONS, (int*)&gamepad->_buttons);
-
-    unsigned int i;
-    for (i = 0; i < gamepad->_joystickCount; ++i)
-    {
-        GP_ASSERT(i < 2);
-
-        int analog[3];
-        switch (i)
-        {
-        case 0:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
-            break;
-        case 1:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
-            break;
-        }
-        
-        // So far we've tested two gamepads with analog sticks on BlackBerry:
-        // the SteelSeries FREE, and the iControlPad.
-        // Both return values between -128 and +127, with the y axis starting from
-        // the top at -128.
-        // 1 / 128 == 0.0078125f
-        // 1 / 127 == 0.0078740157480315f
-        float x = (float)analog[0];
-        float y = -(float)analog[1];
-        x *= (x < 0) ? 0.0078125f : 0.0078740157480315f;
-        y *= (y > 0) ? 0.0078125f : 0.0078740157480315f;
-
-        gamepad->_joysticks[i].set(x, y);        
-    }
-
-    for (i = 0; i < gamepad->_triggerCount; ++i)
-    {
-        GP_ASSERT(i < 2);
-
-        int analog[3];
-        switch (i)
-        {
-        case 0:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
-            break;
-        case 1:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
-            break;
-        }
-
-        float value = (float)analog[2] * 0.0078125f;
-        gamepad->_triggers[i] = value;
-    }
-}
-#else
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-}
-#endif
-
-Platform::Platform(Game* game)
-    : _game(game)
-{
-}
-
-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;
-    }
-
-    if (__screenWindow)
-    {
-        screen_destroy_window(__screenWindow);
-        __screenWindow = NULL;
-    }
-
-    if (__screenEvent)
-    {
-        screen_destroy_event(__screenEvent);
-        __screenEvent = NULL;
-    }
-
-    if (__screenContext)
-    {
-        screen_destroy_context(__screenContext);
-        __screenContext = NULL;
-    }
-}
-
-Platform* Platform::create(Game* game, void* attachToWindow)
-{
-    FileSystem::setResourcePath("./app/native/");
-    Platform* platform = new Platform(game);
-
-    // Query game config
-    int samples = 0;
-    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
-    if (config)
-    {
-        samples = std::max(config->getInt("samples"), 0);
-    }
-
-    __gestureSet = gestures_set_alloc();
-    swipe_gesture_alloc(NULL, gesture_callback, __gestureSet);
-    pinch_gesture_alloc(NULL, gesture_callback, __gestureSet);
-    tap_gesture_alloc(NULL, gesture_callback, __gestureSet);
-
-    bps_initialize();
-
-    // Initialize navigator and orientation
-    static const int SENSOR_RATE = 25000;
-    sensor_set_rate(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, SENSOR_RATE);
-    sensor_set_skip_duplicates(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, true);
-    sensor_request_events(SENSOR_TYPE_AZIMUTH_PITCH_ROLL);
-    navigator_request_events(0);
-    navigator_rotation_lock(true);
-    __orientationAngle = atoi(getenv("ORIENTATION"));
-
-    int rc = 0;
-    int screenFormat = SCREEN_FORMAT_RGBA8888;
-#ifdef __X86__
-    int screenUsage = SCREEN_USAGE_OPENGL_ES2;
-#else
-    int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
-#endif
-    int screenSwapInterval = WINDOW_VSYNC ? 1 : 0;
-    int screenTransparency = SCREEN_TRANSPARENCY_NONE;
-
-    char *width_str = getenv("WIDTH");
-    char *height_str = getenv("HEIGHT");
-
-    // Hard-coded to (0,0).
-    int windowPosition[] =
-    {
-        0, 0
-    };
-
-    EGLint eglConfigCount;
-
-    // Hard-coded to 32-bit/OpenGL ES 2.0.
-    // NOTE: EGL_SAMPLE_BUFFERS and EGL_SAMPLES MUST remain at the beginning of the attribute list
-    // since they are expected to be at indices 0-3 in config fallback code later.
-    EGLint eglConfigAttrs[] =
-    {
-        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
-        EGL_SAMPLES,            samples,
-        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
-    };
-
-    const EGLint eglContextAttrs[] =
-    {
-        EGL_CONTEXT_CLIENT_VERSION,    2,
-        EGL_NONE
-    };
-
-    const EGLint eglSurfaceAttrs[] =
-    {
-        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
-        EGL_NONE
-    };
-
-    // Create the screen context.
-    rc = screen_create_context(&__screenContext, 0);
-    if (rc)
-    {
-        perror("screen_create_context");
-        goto error;
-    }
-
-    // Create the screen window.
-    rc = screen_create_window(&__screenWindow, __screenContext);
-    if (rc)
-    {
-        perror("screen_create_window");
-        goto error;
-    }
-
-    // Set/get any window properties.
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_FORMAT, &screenFormat);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)");
-        goto error;
-    }
-
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_USAGE, &screenUsage);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)");
-        goto error;
-    }
-
-    if (width_str && height_str)
-    {
-        __screenWindowSize[0] = atoi(width_str);
-        __screenWindowSize[1] = atoi(height_str);
-    }
-    else
-    {
-        screen_display_t screen_display;
-        rc = screen_get_window_property_pv(__screenWindow, SCREEN_PROPERTY_DISPLAY, (void **)&screen_display);
-        if (rc)
-        {
-            perror("screen_get_window_property_pv(SCREEN_PROPERTY_DISPLAY)");
-            goto error;
-        }
-
-        screen_display_mode_t screen_mode;
-        rc = screen_get_display_property_pv(screen_display, SCREEN_PROPERTY_MODE, (void**)&screen_mode);
-        if (rc)
-        {
-            perror("screen_get_display_property_pv(SCREEN_PROPERTY_MODE)");
-            goto error;
-        }
-
-        int size[2];
-        rc = screen_get_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, size);
-        if (rc)
-        {
-            perror("screen_get_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
-            goto error;
-        }
-
-        __screenWindowSize[0] = size[0];
-        __screenWindowSize[1] = size[1];
-
-        if ((__orientationAngle == 0) || (__orientationAngle == 180))
-        {
-            if (((screen_mode.width > screen_mode.height) && (size[0] < size[1])) ||
-                ((screen_mode.width < screen_mode.height) && (size[0] > size[1])))
-            {
-                __screenWindowSize[1] = size[0];
-                __screenWindowSize[0] = size[1];
-            }
-        }
-        else if ((__orientationAngle == 90) || (__orientationAngle == 270))
-        {
-            if (((screen_mode.width > screen_mode.height) && (size[0] > size[1])) ||
-                ((screen_mode.width < screen_mode.height) && (size[0] < size[1])))
-            {
-                __screenWindowSize[1] = size[0];
-                __screenWindowSize[0] = size[1];
-            }
-        }
-        else
-        {
-            perror("Navigator returned an unexpected orientation angle.");
-            goto error;
-        }
-
-
-        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_ROTATION, &__orientationAngle);
-        if (rc)
-        {
-            perror("screen_set_window_property_iv(SCREEN_PROPERTY_ROTATION)");
-            goto error;
-        }
-    }
-
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, __screenWindowSize);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
-        goto error;
-    }
-
-    if (windowPosition[0] != 0 || windowPosition[1] != 0)
-    {
-        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_POSITION, windowPosition);
-        if (rc)
-        {
-            perror("screen_set_window_property_iv(SCREEN_PROPERTY_POSITION)");
-            goto error;
-        }
-    }
-
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_TRANSPARENCY, &screenTransparency);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_TRANSPARENCY)");
-        goto error;
-    }
-
-    // Double buffered.
-    rc = screen_create_window_buffers(__screenWindow, 2);
-    if (rc)
-    {
-        perror("screen_create_window_buffers");
-        goto error;
-    }
-
-    // Create screen event object.
-    rc = screen_create_event(&__screenEvent);
-    if (rc)
-    {
-        perror("screen_create_event");
-        goto error;
-    }
-
-    // Request screen events.
-    screen_request_events(__screenContext);
-
-    // Get the EGL display and initialize.
-    __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (__eglDisplay == EGL_NO_DISPLAY)
-    {
-        perror("eglGetDisplay");
-        goto error;
-    }
-    if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
-    {
-        perror("eglInitialize");
-        goto error;
-    }
-
-    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
-    {
-        bool success = false;
-        while (samples)
-        {
-            // Try lowering the MSAA sample count until we find a supported config
-            GP_WARN("Failed to find a valid EGL configuration with EGL samples=%d. Trying samples=%d instead.", samples, samples/2);
-            samples /= 2;
-            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
-            eglConfigAttrs[3] = samples;
-            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-            {
-                success = true;
-                break;
-            }
-        }
-
-        if (!success)
-        {
-            checkErrorEGL("eglChooseConfig");
-            goto error;
-        }
-    }
-
-    __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
-    if (__eglContext == EGL_NO_CONTEXT)
-    {
-        checkErrorEGL("eglCreateContext");
-        goto error;
-    }
-
-    __eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, __screenWindow, eglSurfaceAttrs);
-    if (__eglSurface == EGL_NO_SURFACE)
-    {
-        checkErrorEGL("eglCreateWindowSurface");
-        goto error;
-    }
-
-    if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
-    {
-        checkErrorEGL("eglMakeCurrent");
-        goto error;
-    }
-
-    // Set vsync.
-    eglSwapInterval(__eglDisplay, screenSwapInterval);
-
-    // 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"))
-    {
-        glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
-        glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
-        glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
-        glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
-    }
-
- #ifdef USE_BLACKBERRY_GAMEPAD
-
-    screen_device_t* screenDevs;
-
-    // Discover initial gamepad devices.
-    int count;
-    screen_get_context_property_iv(__screenContext, SCREEN_PROPERTY_DEVICE_COUNT, &count);
-    screenDevs = (screen_device_t*)calloc(count, sizeof(screen_device_t));
-    screen_get_context_property_pv(__screenContext, SCREEN_PROPERTY_DEVICES, (void**)screenDevs);
-
-	for (int i = 0; i < count; i++) 
-    {
-	    int type;
-        screen_get_device_property_iv(screenDevs[i], SCREEN_PROPERTY_TYPE, &type);
-
-        if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
-        {
-            int buttonCount = 0;
-            int joystickCount = 0;
-            int productId;
-            int vendorId;
-            char productString[64];
-            char vendorString[64];
-            queryGamepad(screenDevs[i], &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
-            Platform::gamepadEventConnectedInternal(screenDevs[i], buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
-        }
-	}
-	free(screenDevs);
-#endif
-
-    return platform;
-
-error:
-
-    return NULL;
-}
-
-/**
- * Convert the timespec into milliseconds.
- */
-double timespec2millis(struct timespec *a)
-{
-    GP_ASSERT(a);
-    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
-}
-
-/**
- * Fires a mouse event or a touch event on the game.
- * If the mouse event is not consumed, a touch event is fired instead.
- *
- * @param mouseEvent The mouse event to fire.
- * @param touchEvent The touch event to fire.
- * @param x The x position of the touch in pixels.
- * @param y The y position of the touch in pixels.
- */
-void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
-{
-    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, 0))
-    {
-        Platform::touchEventInternal(touchEvent, x, y, 0);
-    }
-}
-
-int Platform::enterMessagePump()
-{
-    GP_ASSERT(_game);
-
-    int rc;
-    int eventType;
-    int flags;
-    int value;
-    int position[2];
-    int domain;
-    mtouch_event_t touchEvent;
-    bool suspended = false;
-
-    // Get the initial time.
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    __timeStart = timespec2millis(&__timespec);
-    __timeAbsolute = 0L;
-
-    _game->run();
-
-    // Message loop.
-    while (true)
-    {
-        bps_event_t* event = NULL;
-        
-        while (true)
-        {
-            rc = bps_get_event(&event, 1);
-            GP_ASSERT(rc == BPS_SUCCESS);
-
-            if (event == NULL)
-                break;
-
-            domain = bps_event_get_domain(event);
-
-            if (domain == screen_get_domain())
-            {
-                __screenEvent = screen_event_get_event(event);
-                screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_TYPE, &eventType);
-                switch (eventType)
-                {
-                    case SCREEN_EVENT_MTOUCH_TOUCH:
-                    {
-                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
-                        if (__gestureEventsProcessed.any())
-                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
-
-                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, touchEvent.x, touchEvent.y, touchEvent.contact_id);
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_MTOUCH_RELEASE:
-                    {
-                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
-                        if (__gestureEventsProcessed.any())
-                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
-
-                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
-                        }
-                        if (__gestureSwipeRecognized)
-                        {
-                            __gestureSwipeRecognized = false;
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_MTOUCH_MOVE:
-                    {
-                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
-                        if (__gestureEventsProcessed.any())
-                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
-
-                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_POINTER:
-                    {
-                        static int mouse_pressed = 0;
-                        int buttons;
-                        int wheel;
-                        // A move event will be fired unless a button state changed.
-                        bool move = true;
-                        bool left_move = false;
-                        // This is a mouse move event, it is applicable to a device with a usb mouse or simulator.
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
-
-                        // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
-                        if (buttons & SCREEN_LEFT_MOUSE_BUTTON)
-                        {
-                            if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
-                            {
-                                left_move = true;
-                            }
-                            else
-                            {
-                                move = false;
-                                mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
-                                mouseOrTouchEvent(Mouse::MOUSE_PRESS_LEFT_BUTTON, Touch::TOUCH_PRESS, position[0], position[1]);
-                            }
-                        }
-                        else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
-                        {
-                            move = false;
-                            mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
-                            mouseOrTouchEvent(Mouse::MOUSE_RELEASE_LEFT_BUTTON, Touch::TOUCH_RELEASE, position[0], position[1]);
-                        }
-
-                        // Handle right mouse.
-                        if (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
-                        {
-                            if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0)
-                            {
-                                move = false;
-                                mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
-                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
-                            }
-                        }
-                        else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
-                        {
-                            move = false;
-                            mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
-                        }
-
-                        // Handle middle mouse.
-                        if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
-                        {
-                            if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0)
-                            {
-                                move = false;
-                                mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
-                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
-                            }
-                        }
-                        else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
-                        {
-                            move = false;
-                            mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
-                        }
-
-                        // Fire a move event if none of the buttons changed.
-                        if (left_move)
-                        {
-                            mouseOrTouchEvent(Mouse::MOUSE_MOVE, Touch::TOUCH_MOVE, position[0], position[1]);
-                        }
-                        else if (move)
-                        {
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, position[0], position[1], 0);
-                        }
-
-                        // Handle mouse wheel events.
-                        if (wheel)
-                        {
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_KEYBOARD:
-                    {
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
-                        gameplay::Keyboard::KeyEvent evt = (flags & KEY_DOWN) ? gameplay::Keyboard::KEY_PRESS :  gameplay::Keyboard::KEY_RELEASE;
-                        // Suppress key repeats.
-                        if ((flags & KEY_REPEAT) == 0)
-                        {
-                            keyEventInternal(evt, getKey(value));
-                            if (evt == gameplay::Keyboard::KEY_PRESS && (flags & KEY_SYM_VALID))
-                            {
-                                int unicode = getUnicode(value);
-                                if (unicode)
-                                    keyEventInternal(gameplay::Keyboard::KEY_CHAR, unicode);
-                            }
-                        }
-                        break;
-                    }
-#ifdef USE_BLACKBERRY_GAMEPAD
-                    case SCREEN_EVENT_DEVICE:
-                    {
-                        // A device was attached or removed.
-                        screen_device_t device;
-                        int attached;
-
-                        screen_get_event_property_pv(__screenEvent, SCREEN_PROPERTY_DEVICE, (void**)&device);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_ATTACHED, &attached);
-
-                        if (attached)
-                        {
-                            int type;
-                            screen_get_device_property_iv(device, SCREEN_PROPERTY_TYPE, &type);
-                            if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
-                            {
-                                int buttonCount = 0;
-                                int joystickCount = 0;
-                                int productId;
-                                int vendorId;
-                                char productString[64];
-                                char vendorString[64];
-                                queryGamepad(device, &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
-                                Platform::gamepadEventConnectedInternal(device, buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
-                            }
-                        }
-                        else
-                        {
-                            Platform::gamepadEventDisconnectedInternal(device);
-                        }
-
-                        break;
-                    }
-#endif
-                    default:
-                        break;
-                }
-            }
-            else if (domain == navigator_get_domain())
-            {
-                switch (bps_event_get_code(event))
-                {
-                case NAVIGATOR_SWIPE_DOWN:
-                    _game->menuEvent();
-                    break;
-                case NAVIGATOR_WINDOW_STATE:
-                {
-                    navigator_window_state_t state = navigator_event_get_window_state(event);
-                    switch (state)
-                    {
-                    case NAVIGATOR_WINDOW_FULLSCREEN:
-                        if (!__screenFullscreen)
-                            __screenFullscreen = true;
-                        _game->resume();
-                        suspended = false;
-                        break;
-                    case NAVIGATOR_WINDOW_THUMBNAIL:
-                    case NAVIGATOR_WINDOW_INVISIBLE:
-                        if (__screenFullscreen && !suspended)
-                        {
-                            _game->pause();
-                            suspended = true;
-                        }
-                        break;
-                    }
-                    break;
-                }
-                case NAVIGATOR_EXIT:
-                	// Call Game::shutdown directly, instead of Game::exit.
-                	// We need to do this since exit() queues a request to shutdown for the
-                	// next frame, which will never get executed because we are suspended.
-                    _game->shutdown();
-                    break;
-                }
-            }
-            else if (domain == sensor_get_domain())
-            {
-                if (bps_event_get_code(event) == SENSOR_AZIMUTH_PITCH_ROLL_READING)
-                {
-                    float azimuth;
-                    sensor_event_get_apr(event, &azimuth, &__pitch, &__roll);
-                }
-            }
-        }
-
-        // If we are done, then exit.
-        if (_game->getState() == Game::UNINITIALIZED)
-            break;
-
-        if (!suspended)
-        {
-            _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.
-            rc = eglSwapBuffers(__eglDisplay, __eglSurface);
-            if (rc != EGL_TRUE)
-            {
-                _game->shutdown();
-                perror("eglSwapBuffers");
-                break;
-            }
-        }
-    }
-
-    screen_stop_events(__screenContext);
-    bps_shutdown();
-    screen_destroy_context(__screenContext);
-
-    return 0;
-}
-    
-void Platform::signalShutdown() 
-{
-    // nothing to do  
-}
-
-bool Platform::canExit()
-{
-    return true;
-}
-
-unsigned int Platform::getDisplayWidth()
-{
-    return __screenWindowSize[0];
-}
-
-unsigned int Platform::getDisplayHeight()
-{
-    return __screenWindowSize[1];
-}
-
-double Platform::getAbsoluteTime()
-{
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    double now = timespec2millis(&__timespec);
-    __timeAbsolute = now - __timeStart;
-
-    return __timeAbsolute;
-}
-
-void Platform::setAbsoluteTime(double time)
-{
-    __timeAbsolute = time;
-}
-
-bool Platform::isVsync()
-{
-    return __vsync;
-}
-
-void Platform::setVsync(bool enable)
-{
-    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
-    __vsync = enable;
-}
-
-void Platform::swapBuffers()
-{
-    if (__eglDisplay && __eglSurface)
-        eglSwapBuffers(__eglDisplay, __eglSurface);
-}
-
-void Platform::sleep(long ms)
-{
-    usleep(ms * 1000);
-}
-
-void Platform::setMultiTouch(bool enabled)
-{
-    __multiTouch = enabled;
-}
-
-bool Platform::isMultiTouch()
-{
-    return __multiTouch;
-}
-
-void Platform::getAccelerometerValues(float* pitch, float* roll)
-{
-    GP_ASSERT(pitch);
-    GP_ASSERT(roll);
-
-    switch(__orientationAngle)
-    {
-    // Landscape based device adjusting for landscape game mode
-    case 0:
-        if (pitch)
-            *pitch = __pitch;
-        if (roll)
-            *roll = -__roll;
-        break;
-    case 180:
-        if (pitch)
-            *pitch = -__pitch;
-        if (roll)
-            *roll = __roll;
-        break;
-
-    // Portrait based device adjusting for landscape game mode
-    case 90:
-        if (pitch)
-            *pitch = -__roll;
-        if (roll)
-            *roll = -__pitch;
-        break;
-
-    case  270:
-        if (pitch)
-            *pitch = __roll;
-        if (roll)
-            *roll = __pitch;
-        break;
-
-    default:
-        break;
-    }
-}
-
-bool Platform::hasMouse()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setMouseCaptured(bool captured)
-{
-    // not supported
-}
-
-bool Platform::isMouseCaptured()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setCursorVisible(bool visible)
-{
-    // not supported
-}
-
-bool Platform::isCursorVisible()
-{
-    // not supported
-    return false;
-}
-
-void Platform::displayKeyboard(bool display)
-{
-    if (display)
-        virtualkeyboard_show();
-    else
-        virtualkeyboard_hide();
-}
-
-void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
-        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-        Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
-    }
-}
-
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    if (!Form::keyEventInternal(evt, key))
-    {
-        Game::getInstance()->keyEvent(evt, key);
-        Game::getInstance()->getScriptController()->keyEvent(evt, key);
-    }
-}
-
-bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
-{
-    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else
-    {
-        return Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
-    }
-}
-
-void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-                                             unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
-{
-    Gamepad::add(handle, buttonCount, joystickCount, triggerCount, vendorId, productId, vendorString, productString);
-}
-
-void Platform::gamepadEventDisconnectedInternal(GamepadHandle handle)
-{
-    Gamepad::remove(handle);
-}
-
-void Platform::shutdownInternal()
-{
-    Game::getInstance()->shutdown();
-}
-
-bool Platform::isGestureSupported(Gesture::GestureEvent evt)
-{
-    // All are supported no need to test the bitset
-    return true;
-}
-
-void Platform::registerGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.set();
-        break;
-
-    case Gesture::GESTURE_SWIPE:
-    case Gesture::GESTURE_PINCH:
-    case Gesture::GESTURE_TAP:
-        __gestureEventsProcessed.set(evt);
-        break;
-
-    default:
-        break;
-    }
-}
-
-void Platform::unregisterGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.reset();
-        break;
-
-    case Gesture::GESTURE_SWIPE:
-    case Gesture::GESTURE_PINCH:
-    case Gesture::GESTURE_TAP:
-        __gestureEventsProcessed.set(evt, 0);
-        break;
-
-    default:
-        break;
-    }
-}
-    
-bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
-{
-    return __gestureEventsProcessed.test(evt);
-}
-
-bool Platform::launchURL(const char* url)
-{
-    if (url == NULL || *url == '\0')
-        return false;
-
-    return navigator_invoke(url, NULL) == BPS_SUCCESS;
-}
-
-}
-
-#endif
+#ifdef __QNX__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+#include "Form.h"
+#include "ScriptController.h"
+#include <unistd.h>
+#include <sys/keycodes.h>
+#include <screen/screen.h>
+#include <input/screen_helpers.h>
+#include <gestures/set.h>
+#include <gestures/swipe.h>
+#include <gestures/pinch.h>
+#include <gestures/tap.h>
+#include <bps/bps.h>
+#include <bps/event.h>
+#include <bps/screen.h>
+#include <bps/navigator.h>
+#include <bps/sensor.h>
+#include <bps/orientation.h>
+#include <bps/virtualkeyboard.h>
+
+#define TOUCH_COUNT_MAX     4
+
+using namespace std;
+
+int __argc = 0;
+char** __argv = 0;
+struct timespec __timespec;
+static double __timeStart;
+static double __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+static screen_context_t __screenContext;
+static screen_window_t __screenWindow;
+static screen_event_t __screenEvent;
+static int __screenWindowSize[2];
+static bool __screenFullscreen = false;
+static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
+static EGLContext __eglContext = EGL_NO_CONTEXT;
+static EGLSurface __eglSurface = EGL_NO_SURFACE;
+static EGLConfig __eglConfig = 0;
+static int __orientationAngle;
+static bool __multiTouch = false;
+static bool __multiSampling = false;
+static float __pitch;
+static float __roll;
+static const char* __glExtensions;
+static struct gestures_set * __gestureSet;
+static bitset<3> __gestureEventsProcessed;
+static bool __gestureSwipeRecognized = false;
+PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
+PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
+PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
+PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
+
+namespace gameplay
+{
+
+// Gets the Keyboard::Key enumeration constant that corresponds to the given QNX key code.
+static Keyboard::Key getKey(int qnxKeycode)
+{
+    switch (qnxKeycode)
+    {
+    case KEYCODE_SYSREQ:
+        return Keyboard::KEY_SYSREQ;
+    case KEYCODE_BREAK:
+        return Keyboard::KEY_BREAK;
+    case KEYCODE_MENU:
+        return Keyboard::KEY_MENU;
+    case KEYCODE_KP_ENTER:
+        return Keyboard::KEY_KP_ENTER;
+    case KEYCODE_PAUSE:
+        return Keyboard::KEY_PAUSE;
+    case KEYCODE_SCROLL_LOCK:
+        return Keyboard::KEY_SCROLL_LOCK;
+    case KEYCODE_PRINT:
+        return Keyboard::KEY_PRINT;
+    case KEYCODE_ESCAPE:
+        return Keyboard::KEY_ESCAPE;
+    case KEYCODE_BACKSPACE:
+        return Keyboard::KEY_BACKSPACE;
+    case KEYCODE_BACK_TAB:
+        return Keyboard::KEY_BACK_TAB;
+    case KEYCODE_TAB:
+        return Keyboard::KEY_TAB;
+    case KEYCODE_RETURN:
+        return Keyboard::KEY_RETURN;
+    case KEYCODE_CAPS_LOCK:
+        return Keyboard::KEY_CAPS_LOCK;
+    case KEYCODE_LEFT_SHIFT:
+    case KEYCODE_RIGHT_SHIFT:
+        return Keyboard::KEY_SHIFT;
+    case KEYCODE_LEFT_CTRL:
+    case KEYCODE_RIGHT_CTRL:
+        return Keyboard::KEY_CTRL;
+    case KEYCODE_LEFT_ALT:
+    case KEYCODE_RIGHT_ALT:
+        return Keyboard::KEY_ALT;
+    case KEYCODE_LEFT_HYPER:
+    case KEYCODE_RIGHT_HYPER:
+        return Keyboard::KEY_HYPER;
+    case KEYCODE_INSERT:
+        return Keyboard::KEY_INSERT;
+    case KEYCODE_HOME:
+        return Keyboard::KEY_HOME;
+    case KEYCODE_PG_UP:
+        return Keyboard::KEY_PG_UP;
+    case KEYCODE_DELETE:
+        return Keyboard::KEY_DELETE;
+    case KEYCODE_END:
+        return Keyboard::KEY_END;
+    case KEYCODE_PG_DOWN:
+        return Keyboard::KEY_PG_DOWN;
+    case KEYCODE_LEFT:
+        return Keyboard::KEY_LEFT_ARROW;
+    case KEYCODE_RIGHT:
+        return Keyboard::KEY_RIGHT_ARROW;
+    case KEYCODE_UP:
+        return Keyboard::KEY_UP_ARROW;
+    case KEYCODE_DOWN:
+        return Keyboard::KEY_DOWN_ARROW;
+    case KEYCODE_NUM_LOCK:
+        return Keyboard::KEY_NUM_LOCK;
+    case KEYCODE_KP_PLUS:
+        return Keyboard::KEY_KP_PLUS;
+    case KEYCODE_KP_MINUS:
+        return Keyboard::KEY_KP_MINUS;
+    case KEYCODE_KP_MULTIPLY:
+        return Keyboard::KEY_KP_MULTIPLY;
+    case KEYCODE_KP_DIVIDE:
+        return Keyboard::KEY_KP_DIVIDE;
+    case KEYCODE_KP_HOME:
+        return Keyboard::KEY_KP_HOME;
+    case KEYCODE_KP_UP:
+        return Keyboard::KEY_KP_UP;
+    case KEYCODE_KP_PG_UP:
+        return Keyboard::KEY_KP_PG_UP;
+    case KEYCODE_KP_LEFT:
+        return Keyboard::KEY_KP_LEFT;
+    case KEYCODE_KP_FIVE:
+        return Keyboard::KEY_KP_FIVE;
+    case KEYCODE_KP_RIGHT:
+        return Keyboard::KEY_KP_RIGHT;
+    case KEYCODE_KP_END:
+        return Keyboard::KEY_KP_END;
+    case KEYCODE_KP_DOWN:
+        return Keyboard::KEY_KP_DOWN;
+    case KEYCODE_KP_PG_DOWN:
+        return Keyboard::KEY_KP_PG_DOWN;
+    case KEYCODE_KP_INSERT:
+        return Keyboard::KEY_KP_INSERT;
+    case KEYCODE_KP_DELETE:
+        return Keyboard::KEY_KP_DELETE;
+    case KEYCODE_F1:
+        return Keyboard::KEY_F1;
+    case KEYCODE_F2:
+        return Keyboard::KEY_F2;
+    case KEYCODE_F3:
+        return Keyboard::KEY_F3;
+    case KEYCODE_F4:
+        return Keyboard::KEY_F4;
+    case KEYCODE_F5:
+        return Keyboard::KEY_F5;
+    case KEYCODE_F6:
+        return Keyboard::KEY_F6;
+    case KEYCODE_F7:
+        return Keyboard::KEY_F7;
+    case KEYCODE_F8:
+        return Keyboard::KEY_F8;
+    case KEYCODE_F9:
+        return Keyboard::KEY_F9;
+    case KEYCODE_F10:
+        return Keyboard::KEY_F10;
+    case KEYCODE_F11:
+        return Keyboard::KEY_F11;
+    case KEYCODE_F12:
+        return Keyboard::KEY_F12;
+    case KEYCODE_SPACE:
+        return Keyboard::KEY_SPACE;
+    case KEYCODE_RIGHT_PAREN:
+        return Keyboard::KEY_RIGHT_PARENTHESIS;
+    case KEYCODE_ZERO:
+        return Keyboard::KEY_ZERO;
+    case KEYCODE_EXCLAM:
+        return Keyboard::KEY_EXCLAM;
+    case KEYCODE_ONE:
+        return Keyboard::KEY_ONE;
+    case KEYCODE_AT:
+        return Keyboard::KEY_AT;
+    case KEYCODE_TWO:
+        return Keyboard::KEY_TWO;
+    case KEYCODE_NUMBER:
+        return Keyboard::KEY_NUMBER;
+    case KEYCODE_THREE:
+        return Keyboard::KEY_THREE;
+    case KEYCODE_DOLLAR:
+        return Keyboard::KEY_DOLLAR;
+    case KEYCODE_FOUR:
+        return Keyboard::KEY_FOUR;
+    case KEYCODE_PERCENT:
+        return Keyboard::KEY_PERCENT;
+    case KEYCODE_FIVE:
+        return Keyboard::KEY_FIVE;
+    case KEYCODE_CIRCUMFLEX:
+        return Keyboard::KEY_CIRCUMFLEX;
+    case KEYCODE_SIX:
+        return Keyboard::KEY_SIX;
+    case KEYCODE_AMPERSAND:
+        return Keyboard::KEY_AMPERSAND;
+    case KEYCODE_SEVEN:
+        return Keyboard::KEY_SEVEN;
+    case KEYCODE_ASTERISK:
+        return Keyboard::KEY_ASTERISK;
+    case KEYCODE_EIGHT:
+        return Keyboard::KEY_EIGHT;
+    case KEYCODE_LEFT_PAREN:
+        return Keyboard::KEY_LEFT_PARENTHESIS;
+    case KEYCODE_NINE:
+        return Keyboard::KEY_NINE;
+    case KEYCODE_EQUAL:
+        return Keyboard::KEY_EQUAL;
+    case KEYCODE_PLUS:
+        return Keyboard::KEY_PLUS;
+    case KEYCODE_LESS_THAN:
+        return Keyboard::KEY_LESS_THAN;
+    case KEYCODE_COMMA:
+        return Keyboard::KEY_COMMA;
+    case KEYCODE_UNDERSCORE:
+        return Keyboard::KEY_UNDERSCORE;
+    case KEYCODE_MINUS:
+        return Keyboard::KEY_MINUS;
+    case KEYCODE_GREATER_THAN:
+        return Keyboard::KEY_GREATER_THAN;
+    case KEYCODE_PERIOD:
+        return Keyboard::KEY_PERIOD;
+    case KEYCODE_COLON:
+        return Keyboard::KEY_COLON;
+    case KEYCODE_SEMICOLON:
+        return Keyboard::KEY_SEMICOLON;
+    case KEYCODE_QUESTION:
+        return Keyboard::KEY_QUESTION;
+    case KEYCODE_SLASH:
+        return Keyboard::KEY_SLASH;
+    case KEYCODE_GRAVE:
+        return Keyboard::KEY_GRAVE;
+    case KEYCODE_TILDE:
+        return Keyboard::KEY_TILDE;
+    case KEYCODE_LEFT_BRACE:
+        return Keyboard::KEY_LEFT_BRACE;
+    case KEYCODE_LEFT_BRACKET:
+        return Keyboard::KEY_LEFT_BRACKET;
+    case KEYCODE_BAR:
+        return Keyboard::KEY_BAR;
+    case KEYCODE_BACK_SLASH:
+        return Keyboard::KEY_BACK_SLASH;
+    case KEYCODE_RIGHT_BRACE:
+        return Keyboard::KEY_RIGHT_BRACE;
+    case KEYCODE_RIGHT_BRACKET:
+        return Keyboard::KEY_RIGHT_BRACKET;
+    case KEYCODE_QUOTE:
+        return Keyboard::KEY_QUOTE;
+    case KEYCODE_APOSTROPHE:
+        return Keyboard::KEY_APOSTROPHE;
+    case 0x20AC:
+        return Keyboard::KEY_EURO;
+    case KEYCODE_POUND_SIGN:
+        return Keyboard::KEY_POUND;
+    case KEYCODE_YEN_SIGN:
+        return Keyboard::KEY_YEN;
+    case KEYCODE_MIDDLE_DOT:
+        return Keyboard::KEY_MIDDLE_DOT;
+    case KEYCODE_CAPITAL_A:
+        return Keyboard::KEY_CAPITAL_A;
+    case KEYCODE_A:
+        return Keyboard::KEY_A;
+    case KEYCODE_CAPITAL_B:
+        return Keyboard::KEY_CAPITAL_B;
+    case KEYCODE_B:
+        return Keyboard::KEY_B;
+    case KEYCODE_CAPITAL_C:
+        return Keyboard::KEY_CAPITAL_C;
+    case KEYCODE_C:
+        return Keyboard::KEY_C;
+    case KEYCODE_CAPITAL_D:
+        return Keyboard::KEY_CAPITAL_D;
+    case KEYCODE_D:
+        return Keyboard::KEY_D;
+    case KEYCODE_CAPITAL_E:
+        return Keyboard::KEY_CAPITAL_E;
+    case KEYCODE_E:
+        return Keyboard::KEY_E;
+    case KEYCODE_CAPITAL_F:
+        return Keyboard::KEY_CAPITAL_F;
+    case KEYCODE_F:
+        return Keyboard::KEY_F;
+    case KEYCODE_CAPITAL_G:
+        return Keyboard::KEY_CAPITAL_G;
+    case KEYCODE_G:
+        return Keyboard::KEY_G;
+    case KEYCODE_CAPITAL_H:
+        return Keyboard::KEY_CAPITAL_H;
+    case KEYCODE_H:
+        return Keyboard::KEY_H;
+    case KEYCODE_CAPITAL_I:
+        return Keyboard::KEY_CAPITAL_I;
+    case KEYCODE_I:
+        return Keyboard::KEY_I;
+    case KEYCODE_CAPITAL_J:
+        return Keyboard::KEY_CAPITAL_J;
+    case KEYCODE_J:
+        return Keyboard::KEY_J;
+    case KEYCODE_CAPITAL_K:
+        return Keyboard::KEY_CAPITAL_K;
+    case KEYCODE_K:
+        return Keyboard::KEY_K;
+    case KEYCODE_CAPITAL_L:
+        return Keyboard::KEY_CAPITAL_L;
+    case KEYCODE_L:
+        return Keyboard::KEY_L;
+    case KEYCODE_CAPITAL_M:
+        return Keyboard::KEY_CAPITAL_M;
+    case KEYCODE_M:
+        return Keyboard::KEY_M;
+    case KEYCODE_CAPITAL_N:
+        return Keyboard::KEY_CAPITAL_N;
+    case KEYCODE_N:
+        return Keyboard::KEY_N;
+    case KEYCODE_CAPITAL_O:
+        return Keyboard::KEY_CAPITAL_O;
+    case KEYCODE_O:
+        return Keyboard::KEY_O;
+    case KEYCODE_CAPITAL_P:
+        return Keyboard::KEY_CAPITAL_P;
+    case KEYCODE_P:
+        return Keyboard::KEY_P;
+    case KEYCODE_CAPITAL_Q:
+        return Keyboard::KEY_CAPITAL_Q;
+    case KEYCODE_Q:
+        return Keyboard::KEY_Q;
+    case KEYCODE_CAPITAL_R:
+        return Keyboard::KEY_CAPITAL_R;
+    case KEYCODE_R:
+        return Keyboard::KEY_R;
+    case KEYCODE_CAPITAL_S:
+        return Keyboard::KEY_CAPITAL_S;
+    case KEYCODE_S:
+        return Keyboard::KEY_S;
+    case KEYCODE_CAPITAL_T:
+        return Keyboard::KEY_CAPITAL_T;
+    case KEYCODE_T:
+        return Keyboard::KEY_T;
+    case KEYCODE_CAPITAL_U:
+        return Keyboard::KEY_CAPITAL_U;
+    case KEYCODE_U:
+        return Keyboard::KEY_U;
+    case KEYCODE_CAPITAL_V:
+        return Keyboard::KEY_CAPITAL_V;
+    case KEYCODE_V:
+        return Keyboard::KEY_V;
+    case KEYCODE_CAPITAL_W:
+        return Keyboard::KEY_CAPITAL_W;
+    case KEYCODE_W:
+        return Keyboard::KEY_W;
+    case KEYCODE_CAPITAL_X:
+        return Keyboard::KEY_CAPITAL_X;
+    case KEYCODE_X:
+        return Keyboard::KEY_X;
+    case KEYCODE_CAPITAL_Y:
+        return Keyboard::KEY_CAPITAL_Y;
+    case KEYCODE_Y:
+        return Keyboard::KEY_Y;
+    case KEYCODE_CAPITAL_Z:
+        return Keyboard::KEY_CAPITAL_Z;
+    case KEYCODE_Z:
+        return Keyboard::KEY_Z;
+    default:
+        return Keyboard::KEY_NONE;
+    }
+}
+
+/**
+ * Returns the unicode value from the given QNX key code value.
+ * Some non-printable characters also have corresponding unicode values, such as backspace.
+ *
+ * @param qnxKeyCode The keyboard key code.
+ *
+ * @return The unicode value or 0 if the keycode did not represent a unicode key.
+ */
+static int getUnicode(int qnxKeyCode)
+{
+    if (qnxKeyCode >= KEYCODE_PC_KEYS && qnxKeyCode <= UNICODE_PRIVATE_USE_AREA_LAST)
+    {
+        switch (qnxKeyCode)
+        {
+        case KEYCODE_BACKSPACE:
+            return 0x0008;
+        case KEYCODE_TAB:
+            return 0x0009;
+        case KEYCODE_KP_ENTER:
+        case KEYCODE_RETURN:
+            return 0x000A;
+        case KEYCODE_ESCAPE:
+            return 0x001B;
+        // Win32 doesn't consider delete to be a key char.
+        default:
+            return 0;
+        }
+    }
+    return qnxKeyCode;
+}
+
+extern void print(const char* format, ...)
+{
+    GP_ASSERT(format);
+    va_list argptr;
+    va_start(argptr, format);
+    vfprintf(stderr, format, argptr);
+    va_end(argptr);
+}
+
+EGLenum checkErrorEGL(const char* msg)
+{
+    GP_ASSERT(msg);
+    static const char* errmsg[] =
+    {
+        "EGL function failed",
+        "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();
+    fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
+    return error;
+}
+
+void gesture_callback(gesture_base_t* gesture, mtouch_event_t* event, void* param, int async)
+{
+    switch (gesture->type)
+    {
+    case GESTURE_SWIPE:
+        {
+            if ( __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+            {
+                gesture_swipe_t* swipe = (gesture_swipe_t*)gesture;
+                if (!__gestureSwipeRecognized)
+                {
+                    Game::getInstance()->gestureSwipeEvent(swipe->coords.x, swipe->coords.y, swipe->direction);
+                    __gestureSwipeRecognized = true;
+                }
+
+            }
+            break;
+        }
+
+    case GESTURE_PINCH:
+        {
+            if ( __gestureEventsProcessed.test(Gesture::GESTURE_PINCH) )
+            {
+                gesture_pinch_t* pinch = (gesture_pinch_t*)gesture;
+                float dist_x = (float)pinch->last_distance.x - (float)pinch->distance.x;
+                float dist_y = (float)pinch->last_distance.y - (float)pinch->distance.y;
+                float scale = sqrt( (dist_x * dist_x) + (dist_y * dist_y) );
+                Game::getInstance()->gesturePinchEvent(pinch->centroid.x, pinch->centroid.y, scale);
+            }
+            break;
+        }
+
+    case GESTURE_TAP:
+        {
+            if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) )
+            {
+                gesture_tap_t* tap = (gesture_tap_t*)gesture;
+                Game::getInstance()->gestureTapEvent(tap->touch_coords.x, tap->touch_coords.y);
+            }
+            break;
+        }
+
+    default:
+        break;
+
+    }
+}
+
+#ifdef USE_BLACKBERRY_GAMEPAD
+
+static const char* __vendorStrings[] =
+{
+    "SteelSeries",
+    "Nintendo",
+};
+
+static const char* __productStrings[] =
+{
+    "FREE",
+    "Wii Remote",
+};
+
+static const int __VIDs[] = {
+    0x1038,
+    0x057e,
+};
+
+static const int __PIDs[] = {
+    0x1412,
+    0x0306,
+};
+
+static const unsigned int __knownGamepads = 2;
+
+void queryGamepad(GamepadHandle handle, int* buttonCount, int* joystickCount, int* productId, int* vendorId, char* productString, char* vendorString)
+{
+    char id[128];
+    screen_get_device_property_iv(handle, SCREEN_PROPERTY_BUTTON_COUNT, buttonCount);
+    screen_get_device_property_cv(handle, SCREEN_PROPERTY_ID_STRING, 128, id);
+    screen_get_device_property_cv(handle, SCREEN_PROPERTY_PRODUCT, 64, productString);
+    screen_get_device_property_cv(handle, SCREEN_PROPERTY_VENDOR, 64, vendorString);
+
+    // Check for the existence of analog sticks.
+    int analogs[3];
+    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG0, analogs))
+    {
+    	++(*joystickCount);
+    }
+
+    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG1, analogs))
+    {
+    	++(*joystickCount);
+    }
+
+    // ID string format: A-BBBB-CCCC-D.D
+    // A is the device's index
+    // BBBB is the device's Vendor ID (in hexadecimal)
+    // CCCC is the device's Product ID (also in hexadecimal)
+    // D.D is the device's version number
+    char* token = strtok(id, "-");
+    token = strtok(NULL, "-");
+    if (token)
+    {
+	    *vendorId = strtol(token, NULL, 16);
+    }
+
+    token = strtok(NULL, "-");
+    if (token)
+    {
+        *productId = strtol(token, NULL, 16);
+    }
+
+    // For gamepads unknown to BB10,
+    // check VID and PID against gamepads known to gameplay.
+    if (strlen(productString) == 0 || strlen(vendorString) == 0)
+    {
+        for (unsigned int i = 0; i < __knownGamepads; ++i)
+        {
+            if (__VIDs[i] == *vendorId && __PIDs[i] == *productId)
+            {
+            	strcpy(vendorString, __vendorStrings[i]);
+                strcpy(productString, __productStrings[i]);
+            }
+        }
+    }
+}
+
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+	unsigned int buttons;
+    screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_BUTTONS, (int*)&buttons);
+	gamepad->setButtons(buttons);
+
+    unsigned int i;
+    for (i = 0; i < gamepad->_joystickCount; ++i)
+    {
+        GP_ASSERT(i < 2);
+
+        int analog[3];
+        switch (i)
+        {
+        case 0:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
+            break;
+        case 1:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
+            break;
+        }
+        
+        // So far we've tested two gamepads with analog sticks on BlackBerry:
+        // the SteelSeries FREE, and the iControlPad.
+        // Both return values between -128 and +127, with the y axis starting from
+        // the top at -128.
+        // 1 / 128 == 0.0078125f
+        // 1 / 127 == 0.0078740157480315f
+        float x = (float)analog[0];
+        float y = -(float)analog[1];
+        x *= (x < 0) ? 0.0078125f : 0.0078740157480315f;
+        y *= (y > 0) ? 0.0078125f : 0.0078740157480315f;
+
+		gamepad->setJoystickValue(i, x, y);
+    }
+
+    for (i = 0; i < gamepad->_triggerCount; ++i)
+    {
+        GP_ASSERT(i < 2);
+
+        int analog[3];
+        switch (i)
+        {
+        case 0:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
+            break;
+        case 1:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
+            break;
+        }
+
+        float value = (float)analog[2] * 0.0078125f;
+		gamepad->setTriggerValue(i, value);
+    }
+}
+#else
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+}
+#endif
+
+Platform::Platform(Game* game)
+    : _game(game)
+{
+}
+
+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;
+    }
+
+    if (__screenWindow)
+    {
+        screen_destroy_window(__screenWindow);
+        __screenWindow = NULL;
+    }
+
+    if (__screenEvent)
+    {
+        screen_destroy_event(__screenEvent);
+        __screenEvent = NULL;
+    }
+
+    if (__screenContext)
+    {
+        screen_destroy_context(__screenContext);
+        __screenContext = NULL;
+    }
+}
+
+Platform* Platform::create(Game* game, void* attachToWindow)
+{
+    FileSystem::setResourcePath("./app/native/");
+    Platform* platform = new Platform(game);
+
+    // Query game config
+    int samples = 0;
+    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
+    if (config)
+    {
+        samples = std::max(config->getInt("samples"), 0);
+    }
+
+    __gestureSet = gestures_set_alloc();
+    swipe_gesture_alloc(NULL, gesture_callback, __gestureSet);
+    pinch_gesture_alloc(NULL, gesture_callback, __gestureSet);
+    tap_gesture_alloc(NULL, gesture_callback, __gestureSet);
+
+    bps_initialize();
+
+    // Initialize navigator and orientation
+    static const int SENSOR_RATE = 25000;
+    sensor_set_rate(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, SENSOR_RATE);
+    sensor_set_skip_duplicates(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, true);
+    sensor_request_events(SENSOR_TYPE_AZIMUTH_PITCH_ROLL);
+    navigator_request_events(0);
+    navigator_rotation_lock(true);
+    __orientationAngle = atoi(getenv("ORIENTATION"));
+
+    int rc = 0;
+    int screenFormat = SCREEN_FORMAT_RGBA8888;
+#ifdef __X86__
+    int screenUsage = SCREEN_USAGE_OPENGL_ES2;
+#else
+    int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
+#endif
+    int screenSwapInterval = WINDOW_VSYNC ? 1 : 0;
+    int screenTransparency = SCREEN_TRANSPARENCY_NONE;
+
+    char *width_str = getenv("WIDTH");
+    char *height_str = getenv("HEIGHT");
+
+    // Hard-coded to (0,0).
+    int windowPosition[] =
+    {
+        0, 0
+    };
+
+    EGLint eglConfigCount;
+
+    // Hard-coded to 32-bit/OpenGL ES 2.0.
+    // NOTE: EGL_SAMPLE_BUFFERS and EGL_SAMPLES MUST remain at the beginning of the attribute list
+    // since they are expected to be at indices 0-3 in config fallback code later.
+    EGLint eglConfigAttrs[] =
+    {
+        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
+        EGL_SAMPLES,            samples,
+        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
+    };
+    __multiSampling = samples > 0;
+
+    const EGLint eglContextAttrs[] =
+    {
+        EGL_CONTEXT_CLIENT_VERSION,    2,
+        EGL_NONE
+    };
+
+    const EGLint eglSurfaceAttrs[] =
+    {
+        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
+        EGL_NONE
+    };
+
+    // Create the screen context.
+    rc = screen_create_context(&__screenContext, 0);
+    if (rc)
+    {
+        perror("screen_create_context");
+        goto error;
+    }
+
+    // Create the screen window.
+    rc = screen_create_window(&__screenWindow, __screenContext);
+    if (rc)
+    {
+        perror("screen_create_window");
+        goto error;
+    }
+
+    // Set/get any window properties.
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_FORMAT, &screenFormat);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)");
+        goto error;
+    }
+
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_USAGE, &screenUsage);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)");
+        goto error;
+    }
+
+    if (width_str && height_str)
+    {
+        __screenWindowSize[0] = atoi(width_str);
+        __screenWindowSize[1] = atoi(height_str);
+    }
+    else
+    {
+        screen_display_t screen_display;
+        rc = screen_get_window_property_pv(__screenWindow, SCREEN_PROPERTY_DISPLAY, (void **)&screen_display);
+        if (rc)
+        {
+            perror("screen_get_window_property_pv(SCREEN_PROPERTY_DISPLAY)");
+            goto error;
+        }
+
+        screen_display_mode_t screen_mode;
+        rc = screen_get_display_property_pv(screen_display, SCREEN_PROPERTY_MODE, (void**)&screen_mode);
+        if (rc)
+        {
+            perror("screen_get_display_property_pv(SCREEN_PROPERTY_MODE)");
+            goto error;
+        }
+
+        int size[2];
+        rc = screen_get_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, size);
+        if (rc)
+        {
+            perror("screen_get_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
+            goto error;
+        }
+
+        __screenWindowSize[0] = size[0];
+        __screenWindowSize[1] = size[1];
+
+        if ((__orientationAngle == 0) || (__orientationAngle == 180))
+        {
+            if (((screen_mode.width > screen_mode.height) && (size[0] < size[1])) ||
+                ((screen_mode.width < screen_mode.height) && (size[0] > size[1])))
+            {
+                __screenWindowSize[1] = size[0];
+                __screenWindowSize[0] = size[1];
+            }
+        }
+        else if ((__orientationAngle == 90) || (__orientationAngle == 270))
+        {
+            if (((screen_mode.width > screen_mode.height) && (size[0] > size[1])) ||
+                ((screen_mode.width < screen_mode.height) && (size[0] < size[1])))
+            {
+                __screenWindowSize[1] = size[0];
+                __screenWindowSize[0] = size[1];
+            }
+        }
+        else
+        {
+            perror("Navigator returned an unexpected orientation angle.");
+            goto error;
+        }
+
+
+        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_ROTATION, &__orientationAngle);
+        if (rc)
+        {
+            perror("screen_set_window_property_iv(SCREEN_PROPERTY_ROTATION)");
+            goto error;
+        }
+    }
+
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, __screenWindowSize);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
+        goto error;
+    }
+
+    if (windowPosition[0] != 0 || windowPosition[1] != 0)
+    {
+        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_POSITION, windowPosition);
+        if (rc)
+        {
+            perror("screen_set_window_property_iv(SCREEN_PROPERTY_POSITION)");
+            goto error;
+        }
+    }
+
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_TRANSPARENCY, &screenTransparency);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_TRANSPARENCY)");
+        goto error;
+    }
+
+    // Double buffered.
+    rc = screen_create_window_buffers(__screenWindow, 2);
+    if (rc)
+    {
+        perror("screen_create_window_buffers");
+        goto error;
+    }
+
+    // Create screen event object.
+    rc = screen_create_event(&__screenEvent);
+    if (rc)
+    {
+        perror("screen_create_event");
+        goto error;
+    }
+
+    // Request screen events.
+    screen_request_events(__screenContext);
+
+    // Get the EGL display and initialize.
+    __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (__eglDisplay == EGL_NO_DISPLAY)
+    {
+        perror("eglGetDisplay");
+        goto error;
+    }
+    if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
+    {
+        perror("eglInitialize");
+        goto error;
+    }
+
+    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
+    {
+        bool success = false;
+        while (samples)
+        {
+            // Try lowering the MSAA sample count until we find a supported config
+            GP_WARN("Failed to find a valid EGL configuration with EGL samples=%d. Trying samples=%d instead.", samples, samples/2);
+            samples /= 2;
+            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
+            eglConfigAttrs[3] = samples;
+            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+            {
+                success = true;
+                break;
+            }
+        }
+
+        __multiSampling = samples > 0;
+
+        if (!success)
+        {
+            checkErrorEGL("eglChooseConfig");
+            goto error;
+        }
+    }
+
+    __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
+    if (__eglContext == EGL_NO_CONTEXT)
+    {
+        checkErrorEGL("eglCreateContext");
+        goto error;
+    }
+
+    __eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, __screenWindow, eglSurfaceAttrs);
+    if (__eglSurface == EGL_NO_SURFACE)
+    {
+        checkErrorEGL("eglCreateWindowSurface");
+        goto error;
+    }
+
+    if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
+    {
+        checkErrorEGL("eglMakeCurrent");
+        goto error;
+    }
+
+    // Set vsync.
+    eglSwapInterval(__eglDisplay, screenSwapInterval);
+
+    // 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"))
+    {
+        glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
+        glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
+        glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
+        glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
+    }
+
+ #ifdef USE_BLACKBERRY_GAMEPAD
+
+    screen_device_t* screenDevs;
+
+    // Discover initial gamepad devices.
+    int count;
+    screen_get_context_property_iv(__screenContext, SCREEN_PROPERTY_DEVICE_COUNT, &count);
+    screenDevs = (screen_device_t*)calloc(count, sizeof(screen_device_t));
+    screen_get_context_property_pv(__screenContext, SCREEN_PROPERTY_DEVICES, (void**)screenDevs);
+
+	for (int i = 0; i < count; i++) 
+    {
+	    int type;
+        screen_get_device_property_iv(screenDevs[i], SCREEN_PROPERTY_TYPE, &type);
+
+        if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
+        {
+            int buttonCount = 0;
+            int joystickCount = 0;
+            int productId;
+            int vendorId;
+            char productString[64];
+            char vendorString[64];
+            queryGamepad(screenDevs[i], &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
+            Platform::gamepadEventConnectedInternal(screenDevs[i], buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
+        }
+	}
+	free(screenDevs);
+#endif
+
+    return platform;
+
+error:
+
+    return NULL;
+}
+
+/**
+ * Convert the timespec into milliseconds.
+ */
+double timespec2millis(struct timespec *a)
+{
+    GP_ASSERT(a);
+    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
+}
+
+/**
+ * Fires a mouse event or a touch event on the game.
+ * If the mouse event is not consumed, a touch event is fired instead.
+ *
+ * @param mouseEvent The mouse event to fire.
+ * @param touchEvent The touch event to fire.
+ * @param x The x position of the touch in pixels.
+ * @param y The y position of the touch in pixels.
+ */
+void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
+{
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, 0))
+    {
+        Platform::touchEventInternal(touchEvent, x, y, 0);
+    }
+}
+
+int Platform::enterMessagePump()
+{
+    GP_ASSERT(_game);
+
+    int rc;
+    int eventType;
+    int flags;
+    int value;
+    int position[2];
+    int domain;
+    mtouch_event_t touchEvent;
+    bool suspended = false;
+
+    // Get the initial time.
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    __timeStart = timespec2millis(&__timespec);
+    __timeAbsolute = 0L;
+
+    _game->run();
+
+    // Message loop.
+    while (true)
+    {
+        bps_event_t* event = NULL;
+        
+        while (true)
+        {
+            rc = bps_get_event(&event, 1);
+            GP_ASSERT(rc == BPS_SUCCESS);
+
+            if (event == NULL)
+                break;
+
+            domain = bps_event_get_domain(event);
+
+            if (domain == screen_get_domain())
+            {
+                __screenEvent = screen_event_get_event(event);
+                screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_TYPE, &eventType);
+                switch (eventType)
+                {
+                    case SCREEN_EVENT_MTOUCH_TOUCH:
+                    {
+                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
+                        if (__gestureEventsProcessed.any())
+                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
+
+                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, touchEvent.x, touchEvent.y, touchEvent.contact_id);
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_MTOUCH_RELEASE:
+                    {
+                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
+                        if (__gestureEventsProcessed.any())
+                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
+
+                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
+                        }
+                        if (__gestureSwipeRecognized)
+                        {
+                            __gestureSwipeRecognized = false;
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_MTOUCH_MOVE:
+                    {
+                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
+                        if (__gestureEventsProcessed.any())
+                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
+
+                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_POINTER:
+                    {
+                        static int mouse_pressed = 0;
+                        int buttons;
+                        int wheel;
+                        // A move event will be fired unless a button state changed.
+                        bool move = true;
+                        bool left_move = false;
+                        // This is a mouse move event, it is applicable to a device with a usb mouse or simulator.
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
+
+                        // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
+                        if (buttons & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                            {
+                                left_move = true;
+                            }
+                            else
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
+                                mouseOrTouchEvent(Mouse::MOUSE_PRESS_LEFT_BUTTON, Touch::TOUCH_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
+                            mouseOrTouchEvent(Mouse::MOUSE_RELEASE_LEFT_BUTTON, Touch::TOUCH_RELEASE, position[0], position[1]);
+                        }
+
+                        // Handle right mouse.
+                        if (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
+                        }
+
+                        // Handle middle mouse.
+                        if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
+                        }
+
+                        // Fire a move event if none of the buttons changed.
+                        if (left_move)
+                        {
+                            mouseOrTouchEvent(Mouse::MOUSE_MOVE, Touch::TOUCH_MOVE, position[0], position[1]);
+                        }
+                        else if (move)
+                        {
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, position[0], position[1], 0);
+                        }
+
+                        // Handle mouse wheel events.
+                        if (wheel)
+                        {
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_KEYBOARD:
+                    {
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
+                        gameplay::Keyboard::KeyEvent evt = (flags & KEY_DOWN) ? gameplay::Keyboard::KEY_PRESS :  gameplay::Keyboard::KEY_RELEASE;
+                        // Suppress key repeats.
+                        if ((flags & KEY_REPEAT) == 0)
+                        {
+                            keyEventInternal(evt, getKey(value));
+                            if (evt == gameplay::Keyboard::KEY_PRESS && (flags & KEY_SYM_VALID))
+                            {
+                                int unicode = getUnicode(value);
+                                if (unicode)
+                                    keyEventInternal(gameplay::Keyboard::KEY_CHAR, unicode);
+                            }
+                        }
+                        break;
+                    }
+#ifdef USE_BLACKBERRY_GAMEPAD
+                    case SCREEN_EVENT_DEVICE:
+                    {
+                        // A device was attached or removed.
+                        screen_device_t device;
+                        int attached;
+
+                        screen_get_event_property_pv(__screenEvent, SCREEN_PROPERTY_DEVICE, (void**)&device);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_ATTACHED, &attached);
+
+                        if (attached)
+                        {
+                            int type;
+                            screen_get_device_property_iv(device, SCREEN_PROPERTY_TYPE, &type);
+                            if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
+                            {
+                                int buttonCount = 0;
+                                int joystickCount = 0;
+                                int productId;
+                                int vendorId;
+                                char productString[64];
+                                char vendorString[64];
+                                queryGamepad(device, &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
+                                Platform::gamepadEventConnectedInternal(device, buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
+                            }
+                        }
+                        else
+                        {
+                            Platform::gamepadEventDisconnectedInternal(device);
+                        }
+
+                        break;
+                    }
+#endif
+                    default:
+                        break;
+                }
+            }
+            else if (domain == navigator_get_domain())
+            {
+                switch (bps_event_get_code(event))
+                {
+                case NAVIGATOR_SWIPE_DOWN:
+                    _game->menuEvent();
+                    break;
+                case NAVIGATOR_WINDOW_STATE:
+                {
+                    navigator_window_state_t state = navigator_event_get_window_state(event);
+                    switch (state)
+                    {
+                    case NAVIGATOR_WINDOW_FULLSCREEN:
+                        if (!__screenFullscreen)
+                            __screenFullscreen = true;
+                        _game->resume();
+                        suspended = false;
+                        break;
+                    case NAVIGATOR_WINDOW_THUMBNAIL:
+                    case NAVIGATOR_WINDOW_INVISIBLE:
+                        if (__screenFullscreen && !suspended)
+                        {
+                            _game->pause();
+                            suspended = true;
+                        }
+                        break;
+                    }
+                    break;
+                }
+                case NAVIGATOR_EXIT:
+                	// Call Game::shutdown directly, instead of Game::exit.
+                	// We need to do this since exit() queues a request to shutdown for the
+                	// next frame, which will never get executed because we are suspended.
+                    _game->shutdown();
+                    break;
+                }
+            }
+            else if (domain == sensor_get_domain())
+            {
+                if (bps_event_get_code(event) == SENSOR_AZIMUTH_PITCH_ROLL_READING)
+                {
+                    float azimuth;
+                    sensor_event_get_apr(event, &azimuth, &__pitch, &__roll);
+                }
+            }
+        }
+
+        // If we are done, then exit.
+        if (_game->getState() == Game::UNINITIALIZED)
+            break;
+
+        if (!suspended)
+        {
+            _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.
+            rc = eglSwapBuffers(__eglDisplay, __eglSurface);
+            if (rc != EGL_TRUE)
+            {
+                _game->shutdown();
+                perror("eglSwapBuffers");
+                break;
+            }
+        }
+    }
+
+    screen_stop_events(__screenContext);
+    bps_shutdown();
+    screen_destroy_context(__screenContext);
+
+    return 0;
+}
+    
+void Platform::signalShutdown() 
+{
+    // nothing to do  
+}
+
+bool Platform::canExit()
+{
+    return true;
+}
+
+unsigned int Platform::getDisplayWidth()
+{
+    return __screenWindowSize[0];
+}
+
+unsigned int Platform::getDisplayHeight()
+{
+    return __screenWindowSize[1];
+}
+
+double Platform::getAbsoluteTime()
+{
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    double now = timespec2millis(&__timespec);
+    __timeAbsolute = now - __timeStart;
+
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(double time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
+    __vsync = enable;
+}
+
+void Platform::swapBuffers()
+{
+    if (__eglDisplay && __eglSurface)
+        eglSwapBuffers(__eglDisplay, __eglSurface);
+}
+
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
+void Platform::setMultiSampling(bool enabled)
+{
+    if (enabled == __multiSampling)
+    {
+        return;
+    }
+
+    //todo
+
+    __multiSampling = enabled;
+}
+
+bool Platform::isMultiSampling()
+{
+    return __multiSampling;
+}
+
+void Platform::setMultiTouch(bool enabled)
+{
+    __multiTouch = enabled;
+}
+
+bool Platform::isMultiTouch()
+{
+    return __multiTouch;
+}
+
+void Platform::getAccelerometerValues(float* pitch, float* roll)
+{
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
+    switch(__orientationAngle)
+    {
+    // Landscape based device adjusting for landscape game mode
+    case 0:
+        if (pitch)
+            *pitch = __pitch;
+        if (roll)
+            *roll = -__roll;
+        break;
+    case 180:
+        if (pitch)
+            *pitch = -__pitch;
+        if (roll)
+            *roll = __roll;
+        break;
+
+    // Portrait based device adjusting for landscape game mode
+    case 90:
+        if (pitch)
+            *pitch = -__roll;
+        if (roll)
+            *roll = -__pitch;
+        break;
+
+    case  270:
+        if (pitch)
+            *pitch = __roll;
+        if (roll)
+            *roll = __pitch;
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
+
+bool Platform::hasMouse()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setMouseCaptured(bool captured)
+{
+    // not supported
+}
+
+bool Platform::isMouseCaptured()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setCursorVisible(bool visible)
+{
+    // not supported
+}
+
+bool Platform::isCursorVisible()
+{
+    // not supported
+    return false;
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    if (display)
+        virtualkeyboard_show();
+    else
+        virtualkeyboard_hide();
+}
+
+void Platform::shutdownInternal()
+{
+    Game::getInstance()->shutdown();
+}
+
+bool Platform::isGestureSupported(Gesture::GestureEvent evt)
+{
+    // All are supported no need to test the bitset
+    return true;
+}
+
+void Platform::registerGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.set();
+        break;
+
+    case Gesture::GESTURE_SWIPE:
+    case Gesture::GESTURE_PINCH:
+    case Gesture::GESTURE_TAP:
+        __gestureEventsProcessed.set(evt);
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Platform::unregisterGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.reset();
+        break;
+
+    case Gesture::GESTURE_SWIPE:
+    case Gesture::GESTURE_PINCH:
+    case Gesture::GESTURE_TAP:
+        __gestureEventsProcessed.set(evt, 0);
+        break;
+
+    default:
+        break;
+    }
+}
+    
+bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
+{
+    return __gestureEventsProcessed.test(evt);
+}
+
+bool Platform::launchURL(const char* url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    return navigator_invoke(url, NULL) == BPS_SUCCESS;
+}
+
+}
+
+#endif

+ 1460 - 993
gameplay/src/PlatformLinux.cpp

@@ -13,11 +13,69 @@
 #include <sys/time.h>
 #include <GL/glxew.h>
 #include <poll.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <fstream>
 
 #define TOUCH_COUNT_MAX     4
+#define MAX_GAMEPADS 4
 
 using namespace std;
 
+int __argc = 0;
+char** __argv = 0;
+
+enum GamepadAxisInfoFlags
+{
+    GP_AXIS_SKIP = 0x1,
+    GP_AXIS_IS_DPAD = 0x2,
+    GP_AXIS_IS_NEG = 0x4,
+    GP_AXIS_IS_XAXIS = 0x8,
+    GP_AXIS_IS_TRIGGER = 0x10
+};
+
+enum GamepadAxisInfoNormalizeFunction
+{
+    NEG_TO_POS,
+    ZERO_TO_POS
+};
+
+struct GamepadJoystickAxisInfo
+{
+    int axisIndex;
+    unsigned int joystickIndex;
+    unsigned long flags;
+    int mappedPosArg;
+    int mappedNegArg;
+    float deadZone;
+    GamepadAxisInfoNormalizeFunction mapFunc;
+};
+
+struct GamepadInfoEntry
+{
+    unsigned int vendorId;
+    unsigned int productId;
+    const char* productName;
+    unsigned int numberOfJS;
+    unsigned int numberOfAxes;
+    unsigned int numberOfButtons;
+    unsigned int numberOfTriggers;
+
+    GamepadJoystickAxisInfo* axes;
+    long* buttons;
+};
+
+
+struct ConnectedGamepadDevInfo
+{
+    dev_t deviceId;
+    gameplay::GamepadHandle fd;
+    const GamepadInfoEntry& gamepadInfo;
+};
+
 struct timespec __timespec;
 static double __timeStart;
 static double __timeAbsolute;
@@ -27,6 +85,7 @@ static float __roll;
 static bool __mouseCaptured = false;
 static float __mouseCapturePointX = 0;
 static float __mouseCapturePointY = 0;
+static bool __multiSampling = false;
 static bool __cursorVisible = true;
 static Display* __display;
 static Window   __window;
@@ -34,1147 +93,1555 @@ static int __windowSize[2];
 static GLXContext __context;
 static Window __attachToWindow;
 static Atom __atomWmDeleteWindow;
+static list<ConnectedGamepadDevInfo> __connectedGamepads;
 
-namespace gameplay
-{
 
-// Gets the Keyboard::Key enumeration constant that corresponds to the given X11 key symbol.
-static Keyboard::Key getKey(KeySym sym)
+// Gets the gameplay::Keyboard::Key enumeration constant that corresponds to the given X11 key symbol.
+static gameplay::Keyboard::Key getKey(KeySym sym)
 {
     switch (sym)
     {
-    case XK_Sys_Req:
-        return Keyboard::KEY_SYSREQ;
-    case XK_Break:
-        return Keyboard::KEY_BREAK;
-    case XK_Menu :
-        return Keyboard::KEY_MENU;
-    case XK_KP_Enter:
-        return Keyboard::KEY_KP_ENTER;
-    case XK_Pause:
-        return Keyboard::KEY_PAUSE;
-    case XK_Scroll_Lock:
-        return Keyboard::KEY_SCROLL_LOCK;
-    case XK_Print:
-        return Keyboard::KEY_PRINT;
-    case XK_Escape:
-        return Keyboard::KEY_ESCAPE;
-    case XK_BackSpace:
-        return Keyboard::KEY_BACKSPACE;
-    case XK_Tab:
-        return Keyboard::KEY_TAB;
-    case XK_Return:
-        return Keyboard::KEY_RETURN;
-    case XK_Caps_Lock:
-        return Keyboard::KEY_CAPS_LOCK;
-    case XK_Shift_L:
-    case XK_Shift_R:
-        return Keyboard::KEY_SHIFT;
-    case XK_Control_L:
-    case XK_Control_R:
-        return Keyboard::KEY_CTRL;
-    case XK_Alt_L:
-    case XK_Alt_R:
-        return Keyboard::KEY_ALT;
-    case XK_Hyper_L:
-    case XK_Hyper_R:
-        return Keyboard::KEY_HYPER;
-    case XK_Insert:
-        return Keyboard::KEY_INSERT;
-    case XK_Home:
-        return Keyboard::KEY_HOME;
-    case XK_Page_Up:
-        return Keyboard::KEY_PG_UP;
-    case XK_Delete:
-        return Keyboard::KEY_DELETE;
-    case XK_End:
-        return Keyboard::KEY_END;
-    case XK_Page_Down:
-        return Keyboard::KEY_PG_DOWN;
-    case XK_Left:
-        return Keyboard::KEY_LEFT_ARROW;
-    case XK_Right:
-        return Keyboard::KEY_RIGHT_ARROW;
-    case XK_Up:
-        return Keyboard::KEY_UP_ARROW;
-    case XK_Down:
-        return Keyboard::KEY_DOWN_ARROW;
-    case XK_Num_Lock:
-        return Keyboard::KEY_NUM_LOCK;
-    case XK_KP_Add:
-        return Keyboard::KEY_KP_PLUS;
-    case XK_KP_Subtract:
-        return Keyboard::KEY_KP_MINUS;
-    case XK_KP_Multiply:
-        return Keyboard::KEY_KP_MULTIPLY;
-    case XK_KP_Divide:
-        return Keyboard::KEY_KP_DIVIDE;
-    case XK_KP_Home:
-        return Keyboard::KEY_KP_HOME;
-    case XK_KP_Up:
-        return Keyboard::KEY_KP_UP;
-    case XK_KP_Page_Up:
-        return Keyboard::KEY_KP_PG_UP;
-    case XK_KP_Left:
-        return Keyboard::KEY_KP_LEFT;
-    case XK_KP_5:
-        return Keyboard::KEY_KP_FIVE;
-    case XK_KP_Right:
-        return Keyboard::KEY_KP_RIGHT;
-    case XK_KP_End:
-        return Keyboard::KEY_KP_END;
-    case XK_KP_Down:
-        return Keyboard::KEY_KP_DOWN;
-    case XK_KP_Page_Down:
-        return Keyboard::KEY_KP_PG_DOWN;
-    case XK_KP_Insert:
-        return Keyboard::KEY_KP_INSERT;
-    case XK_KP_Delete:
-        return Keyboard::KEY_KP_DELETE;
-    case XK_F1:
-        return Keyboard::KEY_F1;
-    case XK_F2:
-        return Keyboard::KEY_F2;
-    case XK_F3:
-        return Keyboard::KEY_F3;
-    case XK_F4:
-        return Keyboard::KEY_F4;
-    case XK_F5:
-        return Keyboard::KEY_F5;
-    case XK_F6:
-        return Keyboard::KEY_F6;
-    case XK_F7:
-        return Keyboard::KEY_F7;
-    case XK_F8:
-        return Keyboard::KEY_F8;
-    case XK_F9:
-        return Keyboard::KEY_F9;
-    case XK_F10:
-        return Keyboard::KEY_F10;
-    case XK_F11:
-        return Keyboard::KEY_F11;
-    case XK_F12:
-        return Keyboard::KEY_F12;
-    case XK_KP_Space:
-    case XK_space:
-        return Keyboard::KEY_SPACE;
-    case XK_parenright:
-        return Keyboard::KEY_RIGHT_PARENTHESIS;
-    case XK_0:
-        return Keyboard::KEY_ZERO;
-    case XK_exclam:
-        return Keyboard::KEY_EXCLAM;
-    case XK_1:
-        return Keyboard::KEY_ONE;
-    case XK_at:
-        return Keyboard::KEY_AT;
-    case XK_2:
-        return Keyboard::KEY_TWO;
-    case XK_numbersign:
-        return Keyboard::KEY_NUMBER;
-    case XK_3:
-        return Keyboard::KEY_THREE;
-    case XK_dollar:
-        return Keyboard::KEY_DOLLAR;
-    case XK_4:
-        return Keyboard::KEY_FOUR;
-    case XK_percent:
-    case XK_asciicircum :
-        return Keyboard::KEY_CIRCUMFLEX;
-        return Keyboard::KEY_PERCENT;
-    case XK_5:
-        return Keyboard::KEY_FIVE;
-    case XK_6:
-        return Keyboard::KEY_SIX;
-    case XK_ampersand:
-        return Keyboard::KEY_AMPERSAND;
-    case XK_7:
-        return Keyboard::KEY_SEVEN;
-    case XK_asterisk:
-        return Keyboard::KEY_ASTERISK;
-    case XK_8:
-        return Keyboard::KEY_EIGHT;
-    case XK_parenleft:
-        return Keyboard::KEY_LEFT_PARENTHESIS;
-    case XK_9:
-        return Keyboard::KEY_NINE;
-    case XK_equal:
-        return Keyboard::KEY_EQUAL;
-    case XK_plus:
-        return Keyboard::KEY_PLUS;
-    case XK_less:
-        return Keyboard::KEY_LESS_THAN;
-    case XK_comma:
-        return Keyboard::KEY_COMMA;
-    case XK_underscore:
-        return Keyboard::KEY_UNDERSCORE;
-    case XK_minus:
-        return Keyboard::KEY_MINUS;
-    case XK_greater:
-        return Keyboard::KEY_GREATER_THAN;
-    case XK_period:
-        return Keyboard::KEY_PERIOD;
-    case XK_colon:
-        return Keyboard::KEY_COLON;
-    case XK_semicolon:
-        return Keyboard::KEY_SEMICOLON;
-    case XK_question:
-        return Keyboard::KEY_QUESTION;
-    case XK_slash:
-        return Keyboard::KEY_SLASH;
-    case XK_grave:
-        return Keyboard::KEY_GRAVE;
-    case XK_asciitilde:
-        return Keyboard::KEY_TILDE;
-    case XK_braceleft:
-        return Keyboard::KEY_LEFT_BRACE;
-    case XK_bracketleft:
-        return Keyboard::KEY_LEFT_BRACKET;
-    case XK_bar:
-        return Keyboard::KEY_BAR;
-    case XK_backslash:
-        return Keyboard::KEY_BACK_SLASH;
-    case XK_braceright:
-        return Keyboard::KEY_RIGHT_BRACE;
-    case XK_bracketright:
-        return Keyboard::KEY_RIGHT_BRACKET;
-    case XK_quotedbl:
-        return Keyboard::KEY_QUOTE;
-    case XK_apostrophe:
-        return Keyboard::KEY_APOSTROPHE;
-    case XK_EuroSign:
-        return Keyboard::KEY_EURO;
-    case XK_sterling:
-        return Keyboard::KEY_POUND;
-    case XK_yen:
-        return Keyboard::KEY_YEN;
-    case XK_periodcentered:
-        return Keyboard::KEY_MIDDLE_DOT;
-    case XK_A:
-        return Keyboard::KEY_CAPITAL_A;
-    case XK_a:
-        return Keyboard::KEY_A;
-    case XK_B:
-        return Keyboard::KEY_CAPITAL_B;
-    case XK_b:
-        return Keyboard::KEY_B;
-    case XK_C:
-        return Keyboard::KEY_CAPITAL_C;
-    case XK_c:
-        return Keyboard::KEY_C;
-    case XK_D:
-        return Keyboard::KEY_CAPITAL_D;
-    case XK_d:
-        return Keyboard::KEY_D;
-    case XK_E:
-        return Keyboard::KEY_CAPITAL_E;
-    case XK_e:
-        return Keyboard::KEY_E;
-    case XK_F:
-        return Keyboard::KEY_CAPITAL_F;
-    case XK_f:
-        return Keyboard::KEY_F;
-    case XK_G:
-        return Keyboard::KEY_CAPITAL_G;
-    case XK_g:
-        return Keyboard::KEY_G;
-    case XK_H:
-        return Keyboard::KEY_CAPITAL_H;
-    case XK_h:
-        return Keyboard::KEY_H;
-    case XK_I:
-        return Keyboard::KEY_CAPITAL_I;
-    case XK_i:
-        return Keyboard::KEY_I;
-    case XK_J:
-        return Keyboard::KEY_CAPITAL_J;
-    case XK_j:
-        return Keyboard::KEY_J;
-    case XK_K:
-        return Keyboard::KEY_CAPITAL_K;
-    case XK_k:
-        return Keyboard::KEY_K;
-    case XK_L:
-        return Keyboard::KEY_CAPITAL_L;
-    case XK_l:
-        return Keyboard::KEY_L;
-    case XK_M:
-        return Keyboard::KEY_CAPITAL_M;
-    case XK_m:
-        return Keyboard::KEY_M;
-    case XK_N:
-        return Keyboard::KEY_CAPITAL_N;
-    case XK_n:
-        return Keyboard::KEY_N;
-    case XK_O:
-        return Keyboard::KEY_CAPITAL_O;
-    case XK_o:
-        return Keyboard::KEY_O;
-    case XK_P:
-        return Keyboard::KEY_CAPITAL_P;
-    case XK_p:
-        return Keyboard::KEY_P;
-    case XK_Q:
-        return Keyboard::KEY_CAPITAL_Q;
-    case XK_q:
-        return Keyboard::KEY_Q;
-    case XK_R:
-        return Keyboard::KEY_CAPITAL_R;
-    case XK_r:
-        return Keyboard::KEY_R;
-    case XK_S:
-        return Keyboard::KEY_CAPITAL_S;
-    case XK_s:
-        return Keyboard::KEY_S;
-    case XK_T:
-        return Keyboard::KEY_CAPITAL_T;
-    case XK_t:
-        return Keyboard::KEY_T;
-    case XK_U:
-        return Keyboard::KEY_CAPITAL_U;
-    case XK_u:
-        return Keyboard::KEY_U;
-    case XK_V:
-        return Keyboard::KEY_CAPITAL_V;
-    case XK_v:
-        return Keyboard::KEY_V;
-    case XK_W:
-        return Keyboard::KEY_CAPITAL_W;
-    case XK_w:
-        return Keyboard::KEY_W;
-    case XK_X:
-        return Keyboard::KEY_CAPITAL_X;
-    case XK_x:
-        return Keyboard::KEY_X;
-    case XK_Y:
-        return Keyboard::KEY_CAPITAL_Y;
-    case XK_y:
-        return Keyboard::KEY_Y;
-    case XK_Z:
-        return Keyboard::KEY_CAPITAL_Z;
-    case XK_z:
-        return Keyboard::KEY_Z;
-    default:
-        return Keyboard::KEY_NONE;
+        case XK_Sys_Req:
+            return gameplay::Keyboard::KEY_SYSREQ;
+        case XK_Break:
+            return gameplay::Keyboard::KEY_BREAK;
+        case XK_Menu :
+            return gameplay::Keyboard::KEY_MENU;
+        case XK_KP_Enter:
+            return gameplay::Keyboard::KEY_KP_ENTER;
+        case XK_Pause:
+            return gameplay::Keyboard::KEY_PAUSE;
+        case XK_Scroll_Lock:
+            return gameplay::Keyboard::KEY_SCROLL_LOCK;
+        case XK_Print:
+            return gameplay::Keyboard::KEY_PRINT;
+        case XK_Escape:
+            return gameplay::Keyboard::KEY_ESCAPE;
+        case XK_BackSpace:
+            return gameplay::Keyboard::KEY_BACKSPACE;
+        case XK_Tab:
+            return gameplay::Keyboard::KEY_TAB;
+        case XK_Return:
+            return gameplay::Keyboard::KEY_RETURN;
+        case XK_Caps_Lock:
+            return gameplay::Keyboard::KEY_CAPS_LOCK;
+        case XK_Shift_L:
+        case XK_Shift_R:
+            return gameplay::Keyboard::KEY_SHIFT;
+        case XK_Control_L:
+        case XK_Control_R:
+            return gameplay::Keyboard::KEY_CTRL;
+        case XK_Alt_L:
+        case XK_Alt_R:
+            return gameplay::Keyboard::KEY_ALT;
+        case XK_Hyper_L:
+        case XK_Hyper_R:
+            return gameplay::Keyboard::KEY_HYPER;
+        case XK_Insert:
+            return gameplay::Keyboard::KEY_INSERT;
+        case XK_Home:
+            return gameplay::Keyboard::KEY_HOME;
+        case XK_Page_Up:
+            return gameplay::Keyboard::KEY_PG_UP;
+        case XK_Delete:
+            return gameplay::Keyboard::KEY_DELETE;
+        case XK_End:
+            return gameplay::Keyboard::KEY_END;
+        case XK_Page_Down:
+            return gameplay::Keyboard::KEY_PG_DOWN;
+        case XK_Left:
+            return gameplay::Keyboard::KEY_LEFT_ARROW;
+        case XK_Right:
+            return gameplay::Keyboard::KEY_RIGHT_ARROW;
+        case XK_Up:
+            return gameplay::Keyboard::KEY_UP_ARROW;
+        case XK_Down:
+            return gameplay::Keyboard::KEY_DOWN_ARROW;
+        case XK_Num_Lock:
+            return gameplay::Keyboard::KEY_NUM_LOCK;
+        case XK_KP_Add:
+            return gameplay::Keyboard::KEY_KP_PLUS;
+        case XK_KP_Subtract:
+            return gameplay::Keyboard::KEY_KP_MINUS;
+        case XK_KP_Multiply:
+            return gameplay::Keyboard::KEY_KP_MULTIPLY;
+        case XK_KP_Divide:
+            return gameplay::Keyboard::KEY_KP_DIVIDE;
+        case XK_KP_Home:
+            return gameplay::Keyboard::KEY_KP_HOME;
+        case XK_KP_Up:
+            return gameplay::Keyboard::KEY_KP_UP;
+        case XK_KP_Page_Up:
+            return gameplay::Keyboard::KEY_KP_PG_UP;
+        case XK_KP_Left:
+            return gameplay::Keyboard::KEY_KP_LEFT;
+        case XK_KP_5:
+            return gameplay::Keyboard::KEY_KP_FIVE;
+        case XK_KP_Right:
+            return gameplay::Keyboard::KEY_KP_RIGHT;
+        case XK_KP_End:
+            return gameplay::Keyboard::KEY_KP_END;
+        case XK_KP_Down:
+            return gameplay::Keyboard::KEY_KP_DOWN;
+        case XK_KP_Page_Down:
+            return gameplay::Keyboard::KEY_KP_PG_DOWN;
+        case XK_KP_Insert:
+            return gameplay::Keyboard::KEY_KP_INSERT;
+        case XK_KP_Delete:
+            return gameplay::Keyboard::KEY_KP_DELETE;
+        case XK_F1:
+            return gameplay::Keyboard::KEY_F1;
+        case XK_F2:
+            return gameplay::Keyboard::KEY_F2;
+        case XK_F3:
+            return gameplay::Keyboard::KEY_F3;
+        case XK_F4:
+            return gameplay::Keyboard::KEY_F4;
+        case XK_F5:
+            return gameplay::Keyboard::KEY_F5;
+        case XK_F6:
+            return gameplay::Keyboard::KEY_F6;
+        case XK_F7:
+            return gameplay::Keyboard::KEY_F7;
+        case XK_F8:
+            return gameplay::Keyboard::KEY_F8;
+        case XK_F9:
+            return gameplay::Keyboard::KEY_F9;
+        case XK_F10:
+            return gameplay::Keyboard::KEY_F10;
+        case XK_F11:
+            return gameplay::Keyboard::KEY_F11;
+        case XK_F12:
+            return gameplay::Keyboard::KEY_F12;
+        case XK_KP_Space:
+        case XK_space:
+            return gameplay::Keyboard::KEY_SPACE;
+        case XK_parenright:
+            return gameplay::Keyboard::KEY_RIGHT_PARENTHESIS;
+        case XK_0:
+            return gameplay::Keyboard::KEY_ZERO;
+        case XK_exclam:
+            return gameplay::Keyboard::KEY_EXCLAM;
+        case XK_1:
+            return gameplay::Keyboard::KEY_ONE;
+        case XK_at:
+            return gameplay::Keyboard::KEY_AT;
+        case XK_2:
+            return gameplay::Keyboard::KEY_TWO;
+        case XK_numbersign:
+            return gameplay::Keyboard::KEY_NUMBER;
+        case XK_3:
+            return gameplay::Keyboard::KEY_THREE;
+        case XK_dollar:
+            return gameplay::Keyboard::KEY_DOLLAR;
+        case XK_4:
+            return gameplay::Keyboard::KEY_FOUR;
+        case XK_percent:
+        case XK_asciicircum :
+            return gameplay::Keyboard::KEY_CIRCUMFLEX;
+            return gameplay::Keyboard::KEY_PERCENT;
+        case XK_5:
+            return gameplay::Keyboard::KEY_FIVE;
+        case XK_6:
+            return gameplay::Keyboard::KEY_SIX;
+        case XK_ampersand:
+            return gameplay::Keyboard::KEY_AMPERSAND;
+        case XK_7:
+            return gameplay::Keyboard::KEY_SEVEN;
+        case XK_asterisk:
+            return gameplay::Keyboard::KEY_ASTERISK;
+        case XK_8:
+            return gameplay::Keyboard::KEY_EIGHT;
+        case XK_parenleft:
+            return gameplay::Keyboard::KEY_LEFT_PARENTHESIS;
+        case XK_9:
+            return gameplay::Keyboard::KEY_NINE;
+        case XK_equal:
+            return gameplay::Keyboard::KEY_EQUAL;
+        case XK_plus:
+            return gameplay::Keyboard::KEY_PLUS;
+        case XK_less:
+            return gameplay::Keyboard::KEY_LESS_THAN;
+        case XK_comma:
+            return gameplay::Keyboard::KEY_COMMA;
+        case XK_underscore:
+            return gameplay::Keyboard::KEY_UNDERSCORE;
+        case XK_minus:
+            return gameplay::Keyboard::KEY_MINUS;
+        case XK_greater:
+            return gameplay::Keyboard::KEY_GREATER_THAN;
+        case XK_period:
+            return gameplay::Keyboard::KEY_PERIOD;
+        case XK_colon:
+            return gameplay::Keyboard::KEY_COLON;
+        case XK_semicolon:
+            return gameplay::Keyboard::KEY_SEMICOLON;
+        case XK_question:
+            return gameplay::Keyboard::KEY_QUESTION;
+        case XK_slash:
+            return gameplay::Keyboard::KEY_SLASH;
+        case XK_grave:
+            return gameplay::Keyboard::KEY_GRAVE;
+        case XK_asciitilde:
+            return gameplay::Keyboard::KEY_TILDE;
+        case XK_braceleft:
+            return gameplay::Keyboard::KEY_LEFT_BRACE;
+        case XK_bracketleft:
+            return gameplay::Keyboard::KEY_LEFT_BRACKET;
+        case XK_bar:
+            return gameplay::Keyboard::KEY_BAR;
+        case XK_backslash:
+            return gameplay::Keyboard::KEY_BACK_SLASH;
+        case XK_braceright:
+            return gameplay::Keyboard::KEY_RIGHT_BRACE;
+        case XK_bracketright:
+            return gameplay::Keyboard::KEY_RIGHT_BRACKET;
+        case XK_quotedbl:
+            return gameplay::Keyboard::KEY_QUOTE;
+        case XK_apostrophe:
+            return gameplay::Keyboard::KEY_APOSTROPHE;
+        case XK_EuroSign:
+            return gameplay::Keyboard::KEY_EURO;
+        case XK_sterling:
+            return gameplay::Keyboard::KEY_POUND;
+        case XK_yen:
+            return gameplay::Keyboard::KEY_YEN;
+        case XK_periodcentered:
+            return gameplay::Keyboard::KEY_MIDDLE_DOT;
+        case XK_A:
+            return gameplay::Keyboard::KEY_CAPITAL_A;
+        case XK_a:
+            return gameplay::Keyboard::KEY_A;
+        case XK_B:
+            return gameplay::Keyboard::KEY_CAPITAL_B;
+        case XK_b:
+            return gameplay::Keyboard::KEY_B;
+        case XK_C:
+            return gameplay::Keyboard::KEY_CAPITAL_C;
+        case XK_c:
+            return gameplay::Keyboard::KEY_C;
+        case XK_D:
+            return gameplay::Keyboard::KEY_CAPITAL_D;
+        case XK_d:
+            return gameplay::Keyboard::KEY_D;
+        case XK_E:
+            return gameplay::Keyboard::KEY_CAPITAL_E;
+        case XK_e:
+            return gameplay::Keyboard::KEY_E;
+        case XK_F:
+            return gameplay::Keyboard::KEY_CAPITAL_F;
+        case XK_f:
+            return gameplay::Keyboard::KEY_F;
+        case XK_G:
+            return gameplay::Keyboard::KEY_CAPITAL_G;
+        case XK_g:
+            return gameplay::Keyboard::KEY_G;
+        case XK_H:
+            return gameplay::Keyboard::KEY_CAPITAL_H;
+        case XK_h:
+            return gameplay::Keyboard::KEY_H;
+        case XK_I:
+            return gameplay::Keyboard::KEY_CAPITAL_I;
+        case XK_i:
+            return gameplay::Keyboard::KEY_I;
+        case XK_J:
+            return gameplay::Keyboard::KEY_CAPITAL_J;
+        case XK_j:
+            return gameplay::Keyboard::KEY_J;
+        case XK_K:
+            return gameplay::Keyboard::KEY_CAPITAL_K;
+        case XK_k:
+            return gameplay::Keyboard::KEY_K;
+        case XK_L:
+            return gameplay::Keyboard::KEY_CAPITAL_L;
+        case XK_l:
+            return gameplay::Keyboard::KEY_L;
+        case XK_M:
+            return gameplay::Keyboard::KEY_CAPITAL_M;
+        case XK_m:
+            return gameplay::Keyboard::KEY_M;
+        case XK_N:
+            return gameplay::Keyboard::KEY_CAPITAL_N;
+        case XK_n:
+            return gameplay::Keyboard::KEY_N;
+        case XK_O:
+            return gameplay::Keyboard::KEY_CAPITAL_O;
+        case XK_o:
+            return gameplay::Keyboard::KEY_O;
+        case XK_P:
+            return gameplay::Keyboard::KEY_CAPITAL_P;
+        case XK_p:
+            return gameplay::Keyboard::KEY_P;
+        case XK_Q:
+            return gameplay::Keyboard::KEY_CAPITAL_Q;
+        case XK_q:
+            return gameplay::Keyboard::KEY_Q;
+        case XK_R:
+            return gameplay::Keyboard::KEY_CAPITAL_R;
+        case XK_r:
+            return gameplay::Keyboard::KEY_R;
+        case XK_S:
+            return gameplay::Keyboard::KEY_CAPITAL_S;
+        case XK_s:
+            return gameplay::Keyboard::KEY_S;
+        case XK_T:
+            return gameplay::Keyboard::KEY_CAPITAL_T;
+        case XK_t:
+            return gameplay::Keyboard::KEY_T;
+        case XK_U:
+            return gameplay::Keyboard::KEY_CAPITAL_U;
+        case XK_u:
+            return gameplay::Keyboard::KEY_U;
+        case XK_V:
+            return gameplay::Keyboard::KEY_CAPITAL_V;
+        case XK_v:
+            return gameplay::Keyboard::KEY_V;
+        case XK_W:
+            return gameplay::Keyboard::KEY_CAPITAL_W;
+        case XK_w:
+            return gameplay::Keyboard::KEY_W;
+        case XK_X:
+            return gameplay::Keyboard::KEY_CAPITAL_X;
+        case XK_x:
+            return gameplay::Keyboard::KEY_X;
+        case XK_Y:
+            return gameplay::Keyboard::KEY_CAPITAL_Y;
+        case XK_y:
+            return gameplay::Keyboard::KEY_Y;
+        case XK_Z:
+            return gameplay::Keyboard::KEY_CAPITAL_Z;
+        case XK_z:
+            return gameplay::Keyboard::KEY_Z;
+        default:
+            return gameplay::Keyboard::KEY_NONE;
     }
 }
 
 /**
  * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
  */
-static int getUnicode(Keyboard::Key key)
+static int getUnicode(gameplay::Keyboard::Key key)
 {
-
     switch (key)
     {
-    case Keyboard::KEY_BACKSPACE:
-        return 0x0008;
-    case Keyboard::KEY_TAB:
-        return 0x0009;
-    case Keyboard::KEY_RETURN:
-    case Keyboard::KEY_KP_ENTER:
-        return 0x000A;
-    case Keyboard::KEY_ESCAPE:
-        return 0x001B;
-    case Keyboard::KEY_SPACE:
-    case Keyboard::KEY_EXCLAM:
-    case Keyboard::KEY_QUOTE:
-    case Keyboard::KEY_NUMBER:
-    case Keyboard::KEY_DOLLAR:
-    case Keyboard::KEY_PERCENT:
-    case Keyboard::KEY_CIRCUMFLEX:
-    case Keyboard::KEY_AMPERSAND:
-    case Keyboard::KEY_APOSTROPHE:
-    case Keyboard::KEY_LEFT_PARENTHESIS:
-    case Keyboard::KEY_RIGHT_PARENTHESIS:
-    case Keyboard::KEY_ASTERISK:
-    case Keyboard::KEY_PLUS:
-    case Keyboard::KEY_COMMA:
-    case Keyboard::KEY_MINUS:
-    case Keyboard::KEY_PERIOD:
-    case Keyboard::KEY_SLASH:
-    case Keyboard::KEY_ZERO:
-    case Keyboard::KEY_ONE:
-    case Keyboard::KEY_TWO:
-    case Keyboard::KEY_THREE:
-    case Keyboard::KEY_FOUR:
-    case Keyboard::KEY_FIVE:
-    case Keyboard::KEY_SIX:
-    case Keyboard::KEY_SEVEN:
-    case Keyboard::KEY_EIGHT:
-    case Keyboard::KEY_NINE:
-    case Keyboard::KEY_COLON:
-    case Keyboard::KEY_SEMICOLON:
-    case Keyboard::KEY_LESS_THAN:
-    case Keyboard::KEY_EQUAL:
-    case Keyboard::KEY_GREATER_THAN:
-    case Keyboard::KEY_QUESTION:
-    case Keyboard::KEY_AT:
-    case Keyboard::KEY_CAPITAL_A:
-    case Keyboard::KEY_CAPITAL_B:
-    case Keyboard::KEY_CAPITAL_C:
-    case Keyboard::KEY_CAPITAL_D:
-    case Keyboard::KEY_CAPITAL_E:
-    case Keyboard::KEY_CAPITAL_F:
-    case Keyboard::KEY_CAPITAL_G:
-    case Keyboard::KEY_CAPITAL_H:
-    case Keyboard::KEY_CAPITAL_I:
-    case Keyboard::KEY_CAPITAL_J:
-    case Keyboard::KEY_CAPITAL_K:
-    case Keyboard::KEY_CAPITAL_L:
-    case Keyboard::KEY_CAPITAL_M:
-    case Keyboard::KEY_CAPITAL_N:
-    case Keyboard::KEY_CAPITAL_O:
-    case Keyboard::KEY_CAPITAL_P:
-    case Keyboard::KEY_CAPITAL_Q:
-    case Keyboard::KEY_CAPITAL_R:
-    case Keyboard::KEY_CAPITAL_S:
-    case Keyboard::KEY_CAPITAL_T:
-    case Keyboard::KEY_CAPITAL_U:
-    case Keyboard::KEY_CAPITAL_V:
-    case Keyboard::KEY_CAPITAL_W:
-    case Keyboard::KEY_CAPITAL_X:
-    case Keyboard::KEY_CAPITAL_Y:
-    case Keyboard::KEY_CAPITAL_Z:
-    case Keyboard::KEY_LEFT_BRACKET:
-    case Keyboard::KEY_BACK_SLASH:
-    case Keyboard::KEY_RIGHT_BRACKET:
-    case Keyboard::KEY_UNDERSCORE:
-    case Keyboard::KEY_GRAVE:
-    case Keyboard::KEY_A:
-    case Keyboard::KEY_B:
-    case Keyboard::KEY_C:
-    case Keyboard::KEY_D:
-    case Keyboard::KEY_E:
-    case Keyboard::KEY_F:
-    case Keyboard::KEY_G:
-    case Keyboard::KEY_H:
-    case Keyboard::KEY_I:
-    case Keyboard::KEY_J:
-    case Keyboard::KEY_K:
-    case Keyboard::KEY_L:
-    case Keyboard::KEY_M:
-    case Keyboard::KEY_N:
-    case Keyboard::KEY_O:
-    case Keyboard::KEY_P:
-    case Keyboard::KEY_Q:
-    case Keyboard::KEY_R:
-    case Keyboard::KEY_S:
-    case Keyboard::KEY_T:
-    case Keyboard::KEY_U:
-    case Keyboard::KEY_V:
-    case Keyboard::KEY_W:
-    case Keyboard::KEY_X:
-    case Keyboard::KEY_Y:
-    case Keyboard::KEY_Z:
-    case Keyboard::KEY_LEFT_BRACE:
-    case Keyboard::KEY_BAR:
-    case Keyboard::KEY_RIGHT_BRACE:
-    case Keyboard::KEY_TILDE:
-        return key;
-    default:
-        return 0;
+        case gameplay::Keyboard::KEY_BACKSPACE:
+            return 0x0008;
+        case gameplay::Keyboard::KEY_TAB:
+            return 0x0009;
+        case gameplay::Keyboard::KEY_RETURN:
+        case gameplay::Keyboard::KEY_KP_ENTER:
+            return 0x000A;
+        case gameplay::Keyboard::KEY_ESCAPE:
+            return 0x001B;
+        case gameplay::Keyboard::KEY_SPACE:
+        case gameplay::Keyboard::KEY_EXCLAM:
+        case gameplay::Keyboard::KEY_QUOTE:
+        case gameplay::Keyboard::KEY_NUMBER:
+        case gameplay::Keyboard::KEY_DOLLAR:
+        case gameplay::Keyboard::KEY_PERCENT:
+        case gameplay::Keyboard::KEY_CIRCUMFLEX:
+        case gameplay::Keyboard::KEY_AMPERSAND:
+        case gameplay::Keyboard::KEY_APOSTROPHE:
+        case gameplay::Keyboard::KEY_LEFT_PARENTHESIS:
+        case gameplay::Keyboard::KEY_RIGHT_PARENTHESIS:
+        case gameplay::Keyboard::KEY_ASTERISK:
+        case gameplay::Keyboard::KEY_PLUS:
+        case gameplay::Keyboard::KEY_COMMA:
+        case gameplay::Keyboard::KEY_MINUS:
+        case gameplay::Keyboard::KEY_PERIOD:
+        case gameplay::Keyboard::KEY_SLASH:
+        case gameplay::Keyboard::KEY_ZERO:
+        case gameplay::Keyboard::KEY_ONE:
+        case gameplay::Keyboard::KEY_TWO:
+        case gameplay::Keyboard::KEY_THREE:
+        case gameplay::Keyboard::KEY_FOUR:
+        case gameplay::Keyboard::KEY_FIVE:
+        case gameplay::Keyboard::KEY_SIX:
+        case gameplay::Keyboard::KEY_SEVEN:
+        case gameplay::Keyboard::KEY_EIGHT:
+        case gameplay::Keyboard::KEY_NINE:
+        case gameplay::Keyboard::KEY_COLON:
+        case gameplay::Keyboard::KEY_SEMICOLON:
+        case gameplay::Keyboard::KEY_LESS_THAN:
+        case gameplay::Keyboard::KEY_EQUAL:
+        case gameplay::Keyboard::KEY_GREATER_THAN:
+        case gameplay::Keyboard::KEY_QUESTION:
+        case gameplay::Keyboard::KEY_AT:
+        case gameplay::Keyboard::KEY_CAPITAL_A:
+        case gameplay::Keyboard::KEY_CAPITAL_B:
+        case gameplay::Keyboard::KEY_CAPITAL_C:
+        case gameplay::Keyboard::KEY_CAPITAL_D:
+        case gameplay::Keyboard::KEY_CAPITAL_E:
+        case gameplay::Keyboard::KEY_CAPITAL_F:
+        case gameplay::Keyboard::KEY_CAPITAL_G:
+        case gameplay::Keyboard::KEY_CAPITAL_H:
+        case gameplay::Keyboard::KEY_CAPITAL_I:
+        case gameplay::Keyboard::KEY_CAPITAL_J:
+        case gameplay::Keyboard::KEY_CAPITAL_K:
+        case gameplay::Keyboard::KEY_CAPITAL_L:
+        case gameplay::Keyboard::KEY_CAPITAL_M:
+        case gameplay::Keyboard::KEY_CAPITAL_N:
+        case gameplay::Keyboard::KEY_CAPITAL_O:
+        case gameplay::Keyboard::KEY_CAPITAL_P:
+        case gameplay::Keyboard::KEY_CAPITAL_Q:
+        case gameplay::Keyboard::KEY_CAPITAL_R:
+        case gameplay::Keyboard::KEY_CAPITAL_S:
+        case gameplay::Keyboard::KEY_CAPITAL_T:
+        case gameplay::Keyboard::KEY_CAPITAL_U:
+        case gameplay::Keyboard::KEY_CAPITAL_V:
+        case gameplay::Keyboard::KEY_CAPITAL_W:
+        case gameplay::Keyboard::KEY_CAPITAL_X:
+        case gameplay::Keyboard::KEY_CAPITAL_Y:
+        case gameplay::Keyboard::KEY_CAPITAL_Z:
+        case gameplay::Keyboard::KEY_LEFT_BRACKET:
+        case gameplay::Keyboard::KEY_BACK_SLASH:
+        case gameplay::Keyboard::KEY_RIGHT_BRACKET:
+        case gameplay::Keyboard::KEY_UNDERSCORE:
+        case gameplay::Keyboard::KEY_GRAVE:
+        case gameplay::Keyboard::KEY_A:
+        case gameplay::Keyboard::KEY_B:
+        case gameplay::Keyboard::KEY_C:
+        case gameplay::Keyboard::KEY_D:
+        case gameplay::Keyboard::KEY_E:
+        case gameplay::Keyboard::KEY_F:
+        case gameplay::Keyboard::KEY_G:
+        case gameplay::Keyboard::KEY_H:
+        case gameplay::Keyboard::KEY_I:
+        case gameplay::Keyboard::KEY_J:
+        case gameplay::Keyboard::KEY_K:
+        case gameplay::Keyboard::KEY_L:
+        case gameplay::Keyboard::KEY_M:
+        case gameplay::Keyboard::KEY_N:
+        case gameplay::Keyboard::KEY_O:
+        case gameplay::Keyboard::KEY_P:
+        case gameplay::Keyboard::KEY_Q:
+        case gameplay::Keyboard::KEY_R:
+        case gameplay::Keyboard::KEY_S:
+        case gameplay::Keyboard::KEY_T:
+        case gameplay::Keyboard::KEY_U:
+        case gameplay::Keyboard::KEY_V:
+        case gameplay::Keyboard::KEY_W:
+        case gameplay::Keyboard::KEY_X:
+        case gameplay::Keyboard::KEY_Y:
+        case gameplay::Keyboard::KEY_Z:
+        case gameplay::Keyboard::KEY_LEFT_BRACE:
+        case gameplay::Keyboard::KEY_BAR:
+        case gameplay::Keyboard::KEY_RIGHT_BRACE:
+        case gameplay::Keyboard::KEY_TILDE:
+            return key;
+        default:
+            return 0;
     }
 }
-
-extern void print(const char* format, ...)
-{
-    GP_ASSERT(format);
-    va_list argptr;
-    va_start(argptr, format);
-    vfprintf(stderr, format, argptr);
-    va_end(argptr);
-}
-
-Platform::Platform(Game* game) : _game(game)
-{
-}
-
-Platform::~Platform()
+#include <linux/joystick.h> //included here so i avoid the naming conflict between KEY_* defined in input.h and the ones defined in gameplay/Keyboard.h 
+namespace gameplay
 {
-}
+    extern void print(const char* format, ...)
+    {
+        GP_ASSERT(format);
+        va_list argptr;
+        va_start(argptr, format);
+        vfprintf(stderr, format, argptr);
+        va_end(argptr);
+    }
 
-Platform* Platform::create(Game* game, void* attachToWindow)
-{
+    Platform::Platform(Game* game) : _game(game)
+    {
+    }
 
-    GP_ASSERT(game);
-    
-    __attachToWindow = (Window)attachToWindow;
-    FileSystem::setResourcePath("./");
-    Platform* platform = new Platform(game);
-    
-    // Get the display and initialize
-    __display = XOpenDisplay(NULL);
-    if (__display == NULL)
+    Platform::~Platform()
     {
-        perror("XOpenDisplay");
-        return NULL;
     }
-     
-    // Get the window configuration values
-    const char *title = NULL;
-    int __x = 0, __y = 0, __width = 1280, __height = 800, __samples = 0;
-    bool fullscreen = false;
-    if (game->getConfig())
+
+    Platform* Platform::create(Game* game, void* attachToWindow)
     {
-        Properties* config = game->getConfig()->getNamespace("window", true);
-        if (config)
+
+        GP_ASSERT(game);
+
+        __attachToWindow = (Window)attachToWindow;
+        FileSystem::setResourcePath("./");
+        Platform* platform = new Platform(game);
+
+        // Get the display and initialize
+        __display = XOpenDisplay(NULL);
+        if (__display == NULL)
         {
-            // Read window title.
-            title = config->getString("title");
-
-            // Read window rect.
-            int x = config->getInt("x");
-            int y = config->getInt("y");
-            int width = config->getInt("width");
-            int height = config->getInt("height");
-            int samples = config->getInt("samples");
-            fullscreen = config->getBool("fullscreen");
-
-            if (fullscreen && width == 0 && height == 0)
+            perror("XOpenDisplay");
+            return NULL;
+        }
+
+        // Get the window configuration values
+        const char *title = NULL;
+        int __x = 0, __y = 0, __width = 1280, __height = 800, __samples = 0;
+        bool fullscreen = false;
+        if (game->getConfig())
+        {
+            Properties* config = game->getConfig()->getNamespace("window", true);
+            if (config)
             {
-                // Use the screen resolution if fullscreen is true but width and height were not set in the config
-                int screen_num = DefaultScreen(__display);
-                width = DisplayWidth(__display, screen_num);
-                height = DisplayHeight(__display, screen_num);
+                // Read window title.
+                title = config->getString("title");
+
+                // Read window rect.
+                int x = config->getInt("x");
+                int y = config->getInt("y");
+                int width = config->getInt("width");
+                int height = config->getInt("height");
+                int samples = config->getInt("samples");
+                fullscreen = config->getBool("fullscreen");
+
+                if (fullscreen && width == 0 && height == 0)
+                {
+                    // Use the screen resolution if fullscreen is true but width and height were not set in the config
+                    int screen_num = DefaultScreen(__display);
+                    width = DisplayWidth(__display, screen_num);
+                    height = DisplayHeight(__display, screen_num);
+                }
+                if (x != 0) __x = x;
+                if (y != 0) __y = y;
+                if (width != 0) __width = width;
+                if (height != 0) __height = height;
+                if (samples != 0) __samples = samples;
             }
-            if (x != 0) __x = x;
-            if (y != 0) __y = y;
-            if (width != 0) __width = width;
-            if (height != 0) __height = height;
-            if (samples != 0) __samples = samples;
         }
+
+        // GLX version
+        GLint majorGLX, minorGLX = 0;
+        glXQueryVersion(__display, &majorGLX, &minorGLX);
+        if (majorGLX == 1 && minorGLX < 2)
+        {
+            perror("GLX 1.2 or greater is required.");
+            XCloseDisplay(__display);
+            return NULL;
+        }
+        else
+        {
+            printf( "GLX version: %d.%d\n", majorGLX , minorGLX);
+        }
+
+        // Get the GLX Functions
+        glXCreateContextAttribsARB = (GLXContext(*)(Display* dpy, GLXFBConfig config, GLXContext share_context, Bool direct,  const int *attrib_list))glXGetProcAddressARB((GLubyte*)"glXCreateContextAttribsARB");
+        glXChooseFBConfig = (GLXFBConfig*(*)(Display *dpy, int screen, const int *attrib_list, int *nelements))glXGetProcAddressARB((GLubyte*)"glXChooseFBConfig");
+        glXGetVisualFromFBConfig = (XVisualInfo*(*)(Display *dpy, GLXFBConfig config))glXGetProcAddressARB((GLubyte*)"glXGetVisualFromFBConfig");
+        glXGetFBConfigAttrib = (int(*)(Display *dpy, GLXFBConfig config, int attribute, int *value))glXGetProcAddressARB((GLubyte*)"glXGetFBConfigAttrib");
+        glXSwapIntervalEXT = (void(*)(Display* dpy, GLXDrawable drawable, int interval))glXGetProcAddressARB((GLubyte*)"glXSwapIntervalEXT");
+        glXSwapIntervalMESA = (int(*)(unsigned int interval))glXGetProcAddressARB((GLubyte*)"glXSwapIntervalMESA");
+
+        // Get the configs
+        int configAttribs[] = 
+        {
+            GLX_RENDER_TYPE,    GLX_RGBA_BIT,
+            GLX_DRAWABLE_TYPE,  GLX_WINDOW_BIT,
+            GLX_X_RENDERABLE,   True,
+            GLX_DEPTH_SIZE,     24,
+            GLX_STENCIL_SIZE,   8,
+            GLX_RED_SIZE,       8,
+            GLX_GREEN_SIZE,     8,
+            GLX_BLUE_SIZE,      8,
+            GLX_DOUBLEBUFFER,   True,
+            GLX_SAMPLE_BUFFERS, __samples > 0 ? 1 : 0,
+            GLX_SAMPLES,        __samples,
+            0
+        };
+        __multiSampling = __samples > 0;
+
+        GLXFBConfig* configs;
+        int configCount = 0;
+        configs = glXChooseFBConfig(__display, DefaultScreen(__display), configAttribs, &configCount);
+        if ( configCount == 0 || configs == 0 )
+        {
+            perror( "glXChooseFBConfig" );
+            return NULL;
+        }
+
+        // Create the windows
+        XVisualInfo* visualInfo;
+        visualInfo = glXGetVisualFromFBConfig(__display, configs[0]);
+
+        XSetWindowAttributes winAttribs;
+        long eventMask;
+        eventMask = ExposureMask | VisibilityChangeMask | StructureNotifyMask |
+            KeyPressMask | KeyReleaseMask | PointerMotionMask |
+            ButtonPressMask | ButtonReleaseMask |
+            EnterWindowMask | LeaveWindowMask;
+        winAttribs.event_mask = eventMask;
+        winAttribs.border_pixel = 0;
+        winAttribs.bit_gravity = StaticGravity;
+        winAttribs.colormap = XCreateColormap(__display, RootWindow(__display, visualInfo->screen), visualInfo->visual, AllocNone);
+
+        GLint winMask;
+        winMask = CWBorderPixel | CWBitGravity | CWEventMask| CWColormap;
+
+        __window = XCreateWindow(__display, DefaultRootWindow(__display), __x, __y, __width, __height, 0,
+                visualInfo->depth, InputOutput, visualInfo->visual, winMask,
+                &winAttribs);
+
+        // Tell the window manager that it should send the delete window notification through ClientMessage
+        __atomWmDeleteWindow = XInternAtom(__display, "WM_DELETE_WINDOW", False);
+        XSetWMProtocols(__display, __window, &__atomWmDeleteWindow, 1);
+
+        XMapWindow(__display, __window);
+
+        // Send fullscreen atom message to the window; most window managers respect WM_STATE messages
+        // Note: fullscreen mode will use native desktop resolution and won't care about width/height specified
+        if (fullscreen)
+        {
+            XEvent xev;
+            Atom atomWm_state = XInternAtom(__display, "_NET_WM_STATE", False);
+            Atom atomFullscreen = XInternAtom(__display, "_NET_WM_STATE_FULLSCREEN", False);
+
+            memset(&xev, 0, sizeof(xev));
+            xev.type = ClientMessage;
+            xev.xclient.window = __window;
+            xev.xclient.message_type = atomWm_state;
+            xev.xclient.format = 32;
+            xev.xclient.data.l[0] = 1;
+            xev.xclient.data.l[1] = atomFullscreen;
+            xev.xclient.data.l[2] = 0;
+
+            XSendEvent(__display, DefaultRootWindow(__display), false, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+        }
+
+        XStoreName(__display, __window, title ? title : "");
+
+        __context = glXCreateContext(__display, visualInfo, NULL, True);
+        if (!__context)
+        {
+            perror("glXCreateContext");
+            return NULL;
+        }
+        glXMakeCurrent(__display, __window, __context);
+
+        // Use OpenGL 2.x with GLEW
+        glewExperimental = GL_TRUE;
+        GLenum glewStatus = glewInit();
+        if (glewStatus != GLEW_OK)
+        {
+            perror("glewInit");
+            return NULL;
+        }
+
+        // GL Version
+        int versionGL[2] = {-1, -1};
+        glGetIntegerv(GL_MAJOR_VERSION, versionGL);
+        glGetIntegerv(GL_MINOR_VERSION, versionGL + 1);
+        printf("GL version: %d.%d\n", versionGL[0], versionGL[1]);
+
+        // TODO: Get this workings
+        if (glXSwapIntervalEXT)
+            glXSwapIntervalEXT(__display, __window, __vsync ? 1 : 0);
+        else if(glXSwapIntervalMESA)
+            glXSwapIntervalMESA(__vsync ? 1 : 0);
+
+        return platform;
     }
 
-    // GLX version
-    GLint majorGLX, minorGLX = 0;
-    glXQueryVersion(__display, &majorGLX, &minorGLX);
-    if (majorGLX == 1 && minorGLX < 2)
+    void cleanupX11()
     {
-        perror("GLX 1.2 or greater is required.");
-        XCloseDisplay(__display);
-        return NULL;
+        if (__display)
+        {
+            glXMakeCurrent(__display, None, NULL);
+
+            if (__context)
+                glXDestroyContext(__display, __context);
+            if (__window)
+                XDestroyWindow(__display, __window);
+
+            XCloseDisplay(__display);
+        }
     }
-    else
+
+    double timespec2millis(struct timespec *a)
     {
-        printf( "GLX version: %d.%d\n", majorGLX , minorGLX);
+        GP_ASSERT(a);
+        return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
     }
-    
-    // Get the GLX Functions
-    glXCreateContextAttribsARB = (GLXContext(*)(Display* dpy, GLXFBConfig config, GLXContext share_context, Bool direct,  const int *attrib_list))glXGetProcAddressARB((GLubyte*)"glXCreateContextAttribsARB");
-    glXChooseFBConfig = (GLXFBConfig*(*)(Display *dpy, int screen, const int *attrib_list, int *nelements))glXGetProcAddressARB((GLubyte*)"glXChooseFBConfig");
-    glXGetVisualFromFBConfig = (XVisualInfo*(*)(Display *dpy, GLXFBConfig config))glXGetProcAddressARB((GLubyte*)"glXGetVisualFromFBConfig");
-    glXGetFBConfigAttrib = (int(*)(Display *dpy, GLXFBConfig config, int attribute, int *value))glXGetProcAddressARB((GLubyte*)"glXGetFBConfigAttrib");
-    glXSwapIntervalEXT = (void(*)(Display* dpy, GLXDrawable drawable, int interval))glXGetProcAddressARB((GLubyte*)"glXSwapIntervalEXT");
-    glXSwapIntervalMESA = (int(*)(unsigned int interval))glXGetProcAddressARB((GLubyte*)"glXSwapIntervalMESA");
-
-    // Get the configs
-    int configAttribs[] = 
+
+    void updateWindowSize()
     {
-        GLX_RENDER_TYPE,    GLX_RGBA_BIT,
-        GLX_DRAWABLE_TYPE,  GLX_WINDOW_BIT,
-        GLX_X_RENDERABLE,   True,
-        GLX_DEPTH_SIZE,     24,
-        GLX_STENCIL_SIZE,   8,
-        GLX_RED_SIZE,       8,
-        GLX_GREEN_SIZE,     8,
-        GLX_BLUE_SIZE,      8,
-        GLX_DOUBLEBUFFER,   True,
-        GLX_SAMPLE_BUFFERS, __samples > 0 ? 1 : 0,
-        GLX_SAMPLES,        __samples,
-        0
+        GP_ASSERT(__display);
+        GP_ASSERT(__window);
+        XWindowAttributes windowAttrs;
+        XGetWindowAttributes(__display, __window, &windowAttrs);
+        __windowSize[0] = windowAttrs.width;
+        __windowSize[1] = windowAttrs.height;
+    }
+
+
+    //Will need to be dynamic, also should be handled in Gamepad class
+    static const GamepadInfoEntry gamepadLookupTable[] = 
+    {
+        {0x0,0x0,"GENERIC XBOX360",2,6,20,2, 
+                                             (GamepadJoystickAxisInfo[]) {
+                                                                     {0,0,GP_AXIS_IS_XAXIS,0,0,2240,NEG_TO_POS},
+                                                                     {1,0,GP_AXIS_IS_NEG,0,0,2240,NEG_TO_POS},
+                                                                     {2,1,GP_AXIS_IS_XAXIS,0,0,2240,NEG_TO_POS},
+                                                                     {3,1,GP_AXIS_IS_NEG,0,0,2240,NEG_TO_POS},
+                                                                     {4,2,GP_AXIS_IS_TRIGGER,0,0,2240,ZERO_TO_POS},
+                                                                     {5,2,GP_AXIS_IS_TRIGGER,1,0,2240,ZERO_TO_POS},
+                                                                     {-1,0,0,0,0,0,NEG_TO_POS}
+                                                                 },
+                                             (long[]) {
+                                                                          -1,    
+                                                                          -1,  
+                                                                          -1,  
+                                                                          -1, 
+                                                                          -1, 
+                                                                          Gamepad::BUTTON_UP,
+                                                                          Gamepad::BUTTON_DOWN,
+                                                                          Gamepad::BUTTON_LEFT,
+                                                                          Gamepad::BUTTON_RIGHT,
+                                                                          Gamepad::BUTTON_MENU2,
+                                                                          Gamepad::BUTTON_MENU1,
+                                                                          Gamepad::BUTTON_L3,
+                                                                          Gamepad::BUTTON_R3,
+                                                                          Gamepad::BUTTON_L1,
+                                                                          Gamepad::BUTTON_R1,
+                                                                          Gamepad::BUTTON_MENU3,
+                                                                          Gamepad::BUTTON_A,
+                                                                          Gamepad::BUTTON_B,
+                                                                          Gamepad::BUTTON_X,
+                                                                          Gamepad::BUTTON_Y
+                                                                         }
+        },
+        {0x79,0x6,"DragonRise Inc. Generic USB Joystick",2,7,12,0, 
+                                             (GamepadJoystickAxisInfo[]) {
+                                                                     {0,1, GP_AXIS_IS_XAXIS,0,0,2240,NEG_TO_POS},
+                                                                     {1,1,GP_AXIS_IS_NEG,0,0,2240,NEG_TO_POS},
+                                                                     {2,0,GP_AXIS_SKIP,0,0,2240,NEG_TO_POS},
+                                                                     {3,0,GP_AXIS_IS_XAXIS,0,0,2240,NEG_TO_POS},
+                                                                     {4,0,GP_AXIS_IS_NEG,0,0,2240,NEG_TO_POS},
+                                                                     {5,2,GP_AXIS_IS_DPAD, Gamepad::BUTTON_RIGHT, Gamepad::BUTTON_LEFT,2240,NEG_TO_POS},
+                                                                     {6,2,GP_AXIS_IS_DPAD, Gamepad::BUTTON_DOWN, Gamepad::BUTTON_UP,2240,NEG_TO_POS},
+                                                                     {-1,0,0,0,0,0,NEG_TO_POS}
+                                                                 },
+                                             (long[]) {
+                                                                          Gamepad::BUTTON_Y,    
+                                                                          Gamepad::BUTTON_B,  
+                                                                          Gamepad::BUTTON_A,  
+                                                                          Gamepad::BUTTON_X, 
+                                                                          Gamepad::BUTTON_L1, 
+                                                                          Gamepad::BUTTON_R1, 
+                                                                          Gamepad::BUTTON_L2,    
+                                                                          Gamepad::BUTTON_R2,   
+                                                                          Gamepad::BUTTON_MENU1,   
+                                                                          Gamepad::BUTTON_MENU2,   
+                                                                          Gamepad::BUTTON_L3,
+                                                                          Gamepad::BUTTON_R3,
+                                                                         }
+        },
+        {0x54c,0x268,"Sony Corp. Batoh Device / PlayStation 3 Controller",2,27,19,2, 
+                                             (GamepadJoystickAxisInfo[]) {
+                                                                     {0,0,GP_AXIS_IS_XAXIS,0,0,2240,NEG_TO_POS},
+                                                                     {1,0,GP_AXIS_IS_NEG,0,0,2240,NEG_TO_POS},
+                                                                     {2,1,GP_AXIS_IS_XAXIS,0,0,2240,NEG_TO_POS},
+                                                                     {3,1,GP_AXIS_IS_NEG,0,0,2240,NEG_TO_POS},
+                                                                     {12,1,GP_AXIS_IS_TRIGGER,0,0,2240,ZERO_TO_POS},
+                                                                     {13,2,GP_AXIS_IS_TRIGGER,1,0,2240,ZERO_TO_POS},
+                                                                     {-1,0,0,0,0,0,NEG_TO_POS}
+                                                                 },
+                                             (long[]) {
+                                                                          Gamepad::BUTTON_MENU1,    
+                                                                          Gamepad::BUTTON_L3,  
+                                                                          Gamepad::BUTTON_R3,  
+                                                                          Gamepad::BUTTON_MENU2, 
+                                                                          Gamepad::BUTTON_UP, 
+                                                                          Gamepad::BUTTON_RIGHT, 
+                                                                          Gamepad::BUTTON_DOWN,    
+                                                                          Gamepad::BUTTON_LEFT,   
+                                                                          Gamepad::BUTTON_L2,  //Use Trigger Instead of BUTTON_L2? or both should be called
+                                                                          Gamepad::BUTTON_R2,  //Use Trigger Instead of BUTTON_R2? or both should be called                                                                        
+                                                                          Gamepad::BUTTON_L1,
+                                                                          Gamepad::BUTTON_R1,
+                                                                          Gamepad::BUTTON_Y,    
+                                                                          Gamepad::BUTTON_B,  
+                                                                          Gamepad::BUTTON_A,  
+                                                                          Gamepad::BUTTON_X, 
+                                                                          Gamepad::BUTTON_MENU3, 
+                                                                          -1,
+                                                                          -1
+                                                                         }
+        }
     };
-    GLXFBConfig* configs;
-    int configCount = 0;
-    configs = glXChooseFBConfig(__display, DefaultScreen(__display), configAttribs, &configCount);
-    if ( configCount == 0 || configs == 0 )
+
+    bool isGamepadDevRegistered(dev_t devId)
     {
-        perror( "glXChooseFBConfig" );
-        return NULL;
+        for(list<ConnectedGamepadDevInfo>::iterator it = __connectedGamepads.begin(); it != __connectedGamepads.end();++it)
+        {
+            if(devId == (*it).deviceId) return true;
+        }
+        return false;
     }
 
-    // Create the windows
-    XVisualInfo* visualInfo;
-    visualInfo = glXGetVisualFromFBConfig(__display, configs[0]);
-    
-    XSetWindowAttributes winAttribs;
-    long eventMask;
-    eventMask = ExposureMask | VisibilityChangeMask | StructureNotifyMask |
-                KeyPressMask | KeyReleaseMask | PointerMotionMask |
-                ButtonPressMask | ButtonReleaseMask |
-                EnterWindowMask | LeaveWindowMask;
-    winAttribs.event_mask = eventMask;
-    winAttribs.border_pixel = 0;
-    winAttribs.bit_gravity = StaticGravity;
-    winAttribs.colormap = XCreateColormap(__display, RootWindow(__display, visualInfo->screen), visualInfo->visual, AllocNone);
-
-    GLint winMask;
-    winMask = CWBorderPixel | CWBitGravity | CWEventMask| CWColormap;
-   
-    __window = XCreateWindow(__display, DefaultRootWindow(__display), __x, __y, __width, __height, 0,
-                            visualInfo->depth, InputOutput, visualInfo->visual, winMask,
-                            &winAttribs);
-
-    // Tell the window manager that it should send the delete window notification through ClientMessage
-    __atomWmDeleteWindow = XInternAtom(__display, "WM_DELETE_WINDOW", False);
-    XSetWMProtocols(__display, __window, &__atomWmDeleteWindow, 1);
-
-    XMapWindow(__display, __window);
-
-    // Send fullscreen atom message to the window; most window managers respect WM_STATE messages
-    // Note: fullscreen mode will use native desktop resolution and won't care about width/height specified
-    if (fullscreen)
+    void closeGamepad(const ConnectedGamepadDevInfo& gamepadDevInfo)
     {
-        XEvent xev;
-        Atom atomWm_state = XInternAtom(__display, "_NET_WM_STATE", False);
-        Atom atomFullscreen = XInternAtom(__display, "_NET_WM_STATE_FULLSCREEN", False);
-
-        memset(&xev, 0, sizeof(xev));
-        xev.type = ClientMessage;
-        xev.xclient.window = __window;
-        xev.xclient.message_type = atomWm_state;
-        xev.xclient.format = 32;
-        xev.xclient.data.l[0] = 1;
-        xev.xclient.data.l[1] = atomFullscreen;
-        xev.xclient.data.l[2] = 0;
-
-        XSendEvent(__display, DefaultRootWindow(__display), false, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+        ::close(gamepadDevInfo.fd);
     }
-    
-    XStoreName(__display, __window, title ? title : "");
 
-    __context = glXCreateContext(__display, visualInfo, NULL, True);
-    if (!__context)
+    void unregisterGamepad(GamepadHandle handle)
     {
-        perror("glXCreateContext");
-        return NULL;
+        for(list<ConnectedGamepadDevInfo>::iterator it = __connectedGamepads.begin(); it != __connectedGamepads.end();++it)
+        {
+            if(handle == (*it).fd)
+            {
+                closeGamepad(*it);
+                __connectedGamepads.erase(it);
+                return;
+            }
+        }
     }
-    glXMakeCurrent(__display, __window, __context);
 
-    // Use OpenGL 2.x with GLEW
-    glewExperimental = GL_TRUE;
-    GLenum glewStatus = glewInit();
-    if (glewStatus != GLEW_OK)
+    void closeAllGamepads()
     {
-        perror("glewInit");
-        return NULL;
+        for(list<ConnectedGamepadDevInfo>::iterator it = __connectedGamepads.begin(); it != __connectedGamepads.end();++it)
+        {
+            closeGamepad(*it);
+            __connectedGamepads.erase(it);
+        }
     }
 
-    // GL Version
-    int versionGL[2] = {-1, -1};
-    glGetIntegerv(GL_MAJOR_VERSION, versionGL);
-    glGetIntegerv(GL_MINOR_VERSION, versionGL + 1);
-    printf("GL version: %d.%d\n", versionGL[0], versionGL[1]);
-
-    // TODO: Get this workings
-    if (glXSwapIntervalEXT)
-        glXSwapIntervalEXT(__display, __window, __vsync ? 1 : 0);
-    else if(glXSwapIntervalMESA)
-        glXSwapIntervalMESA(__vsync ? 1 : 0);
- 
-    return platform;
-}
+    const GamepadInfoEntry& getGamepadMappedInfo(unsigned int vendorId, unsigned int productId, unsigned int numberOfAxes, unsigned int numberOfButtons)
+    {
+        for(int i=0;i<sizeof(gamepadLookupTable)/sizeof(GamepadInfoEntry);i++)
+        {
+            const GamepadInfoEntry& curEntry = gamepadLookupTable[i];
+            if(curEntry.vendorId == vendorId && curEntry.productId == productId)
+            {
+                return curEntry;
+            }
+        }
 
-void cleanupX11()
-{
-    if (__display)
+        for(int i=0;i<sizeof(gamepadLookupTable)/sizeof(GamepadInfoEntry);i++)
+        {
+            const GamepadInfoEntry& curEntry = gamepadLookupTable[i];
+            if(curEntry.vendorId == 0 && curEntry.productId == 0 && curEntry.numberOfAxes == numberOfAxes && curEntry.numberOfButtons == numberOfButtons)
+            {
+                return curEntry;
+            }
+        }
+
+        return gamepadLookupTable[0];
+    }
+
+    const GamepadInfoEntry& getGamepadMappedInfo(const GamepadHandle handle)
     {
-        glXMakeCurrent(__display, None, NULL);
+        GP_ASSERT(pad);
 
-        if (__context)
-             glXDestroyContext(__display, __context);
-        if (__window)
-             XDestroyWindow(__display, __window);
+        for(list<ConnectedGamepadDevInfo>::iterator it = __connectedGamepads.begin(); it != __connectedGamepads.end();++it)
+        {
+            if(handle == (*it).fd)
+            {
+                return it->gamepadInfo;
+            }
+        }
+        GP_WARN("Gamepad not connected but yet trying to get its data. Falling back to generic one.");
+        return gamepadLookupTable[0];
+    }
 
-         XCloseDisplay(__display);
+    const GamepadJoystickAxisInfo* tryGetGamepadMappedAxisInfo(const GamepadInfoEntry& gpinfo, unsigned int axisNumber)
+    {
+        if(axisNumber >= 0 && axisNumber < gpinfo.numberOfAxes)
+        {
+            int i = 0;
+            while(true)
+            {
+                const GamepadJoystickAxisInfo* curAxisInfo = &gpinfo.axes[i++];
+                if(curAxisInfo->axisIndex == axisNumber)
+                    return curAxisInfo;
+                else if(curAxisInfo->axisIndex < 0)
+                    return NULL;
+            }
+        }
+        return NULL;
     }
-}
 
-double timespec2millis(struct timespec *a)
-{
-    GP_ASSERT(a);
-    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
-}
+    bool tryGetGamepadMappedButton(const GamepadInfoEntry& gpinfo, unsigned long btnNumber, long& outMap)
+    {
+        if(btnNumber >= 0 && btnNumber < gpinfo.numberOfButtons )
+        {
+            if(gpinfo.buttons[btnNumber] >= 0)
+            {
+                outMap = gpinfo.buttons[btnNumber];
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+        GP_WARN("Unmapped gamepad button: %u.",btnNumber);
+        return false;
+    }
 
-void updateWindowSize()
-{
-    GP_ASSERT(__display);
-    GP_ASSERT(__window);
-    XWindowAttributes windowAttrs;
-    XGetWindowAttributes(__display, __window, &windowAttrs);
-    __windowSize[0] = windowAttrs.width;
-    __windowSize[1] = windowAttrs.height;
-}
+    unsigned int readIntegerGamepadIdPropery(const char* sysFSIdPath, const char* propertyName)
+    {
+        unsigned int ret = 0;
+        try {
+            ifstream propStream;
+            propStream.open((string(sysFSIdPath) + propertyName).c_str(),ifstream::in);
+            propStream >> std::hex >> ret;
+            propStream.close();
+        } catch (exception e) {
+            GP_WARN("Could not read propery from SysFS for Gamepad: %s", propertyName);
+        }
+        return ret;
+    }
 
-int Platform::enterMessagePump()
-{
-    GP_ASSERT(_game);
-
-    updateWindowSize();
- 
-    static const float ACCELEROMETER_X_FACTOR = 90.0f / __windowSize[0];
-    static const float ACCELEROMETER_Y_FACTOR = 90.0f / __windowSize[1];
-    static int lx = 0;
-    static int ly = 0;
-    static bool shiftDown = false;
-    static bool capsOn = false;
-    static XEvent evt;
-
-    // Get the initial time.
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    __timeStart = timespec2millis(&__timespec);
-    __timeAbsolute = 0L;
-
-    // Run the game.
-    _game->run();
-
-    // Setup select for message handling (to allow non-blocking)
-    int x11_fd = ConnectionNumber(__display);
-    
-    pollfd xpolls[1];
-    xpolls[0].fd = x11_fd;
-    xpolls[0].events = POLLIN|POLLPRI;
-  
-    // Message loop.
-    while (true)
+    bool isBlackListed(unsigned int vendorId, unsigned int productId)
     {
-        poll( xpolls, 1, 16 );
-        // handle all pending events in one block
-        while (XPending(__display))
+        switch(vendorId)
         {
-           XNextEvent(__display, &evt);
-        
-            switch (evt.type)
+            case 0x0e0f: //virtual machine devices
+                if(productId == 0x0003) // Virtual Mouse
+                    return true;
+        }
+        return false;
+    }
+
+    void handleConnectedGamepad(dev_t devId, const char* devPath, const char* sysFSIdPath)
+    {
+        GP_ASSERT(devPath);
+
+        unsigned int vendorId =readIntegerGamepadIdPropery(sysFSIdPath,"vendor");
+        unsigned int productId =readIntegerGamepadIdPropery(sysFSIdPath,"product");
+
+        if(isBlackListed(vendorId,productId)) return;
+
+        GamepadHandle handle = ::open(devPath,O_RDONLY | O_NONBLOCK);
+        if(handle < 0)
+        {
+            GP_WARN("Could not open Gamepad device.");
+            return;
+        }
+
+        if(!(fcntl(handle, F_GETFL) != -1 || errno != EBADF))
+            return;
+
+        char axesNum, btnsNum, name[256];
+        ioctl(handle, JSIOCGNAME(256), name);
+        ioctl (handle, JSIOCGAXES, &axesNum);
+        ioctl (handle, JSIOCGBUTTONS, &btnsNum);
+
+        const GamepadInfoEntry& gpInfo = getGamepadMappedInfo(vendorId,productId,(unsigned int)axesNum,(unsigned int)btnsNum);
+        unsigned int numJS = gpInfo.numberOfJS;
+        unsigned int numTR = gpInfo.numberOfTriggers;
+
+
+        Platform::gamepadEventConnectedInternal(handle,btnsNum,numJS,numTR,vendorId,productId,"",name);
+
+        ConnectedGamepadDevInfo info = {devId,handle,gpInfo}; 
+        __connectedGamepads.push_back(info);
+    }
+
+    static float normalizeJoystickAxis(int axisValue, int deadZone, bool zeroToOne)
+    {
+        int absAxisValue = 0;
+        if(zeroToOne)
+            absAxisValue = (axisValue + 32767) / 2.0;
+        else
+             absAxisValue = abs(axisValue);
+
+        if (absAxisValue < deadZone)
+        {
+            return 0.0f;
+        }
+        else
+        {
+            int maxVal = 0;
+            int value = 0;
+            if(!zeroToOne)
             {
-            case ClientMessage:
+                value = axisValue;
+                if (value < 0)
                 {
-                    // Handle destroy window message correctly
-                    if (evt.xclient.data.l[0] == __atomWmDeleteWindow)
-                    {
-                        _game->exit();
-                    }
+                    value = -1;
+                    maxVal = 32768;
                 }
-                break;
-            case DestroyNotify :
+                else if (value > 0)
                 {
-                    cleanupX11();
-                    exit(0);
+                    value = 1;
+                    maxVal = 32767;
                 }
-                break;
-
-            case Expose:
+                else
                 {
-                    updateWindowSize();
+                    return 0.0f;
                 }
-                break;
+            }
+            else
+            {
+                value = 1;
+                maxVal = 32767;
+            }
+
+            float ret = value * (absAxisValue - deadZone) / (float)(maxVal - deadZone);
+            return ret;
+        }
+    }
+
+    void enumGamepads()
+    {
+        const int maxDevs = 16;
+        const char* devPathFormat = "/dev/input/js%u";
+        const char* sysfsPathFormat = "/sys/class/input/js%u/device/id/";
+        char curDevPath[20];
 
-            case KeyPress:
+        for(int i=0;i<maxDevs;i++)
+        {
+            sprintf(curDevPath,devPathFormat,i);
+            struct stat gpstat;
+            if(::stat(curDevPath,&gpstat) == 0)
+            {
+                dev_t devid = gpstat.st_rdev;
+                if(!isGamepadDevRegistered(devid))
                 {
-                    KeySym sym = XLookupKeysym(&evt.xkey, (evt.xkey.state & shiftDown) ? 1 : 0);
+                    char cursysFSPath[35];
+                    sprintf(cursysFSPath,sysfsPathFormat,i);
+                    handleConnectedGamepad(devid,curDevPath,cursysFSPath);
+                }
+            }
+        }
+    }
 
+    void gamepadHandlingLoop()
+    {
+        enumGamepads();
+    }
 
-                    //TempSym needed because XConvertCase operates on two keysyms: One lower and the other upper, we are only interested in the upper case
-                    KeySym tempSym;
-                    if (capsOn && !shiftDown)
-                        XConvertCase(sym,  &tempSym, &sym);
+    int Platform::enterMessagePump()
+    {
+        GP_ASSERT(_game);
 
-                    Keyboard::Key key = getKey(sym);
-                    gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, key);
+        updateWindowSize();
 
-                    if (key == Keyboard::KEY_CAPS_LOCK)
-                        capsOn = !capsOn;
-                    if (key == Keyboard::KEY_SHIFT)
-                        shiftDown = true;
+        static const float ACCELEROMETER_X_FACTOR = 90.0f / __windowSize[0];
+        static const float ACCELEROMETER_Y_FACTOR = 90.0f / __windowSize[1];
+        static int lx = 0;
+        static int ly = 0;
+        static bool shiftDown = false;
+        static bool capsOn = false;
+        static XEvent evt;
 
-                    if (int character = getUnicode(key))
-                        gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, character);
+        // Get the initial time.
+        clock_gettime(CLOCK_REALTIME, &__timespec);
+        __timeStart = timespec2millis(&__timespec);
+        __timeAbsolute = 0L;
 
-                }
-                break;
+        // Run the game.
+        _game->run();
 
-            case KeyRelease:
-                {
-                    //detect and drop repeating keystrokes (no other way to do this using the event interface)
-                    XEvent next;
-                    if ( XPending(__display) )
-                    {
-                        XPeekEvent(__display,&next);
-                        if ( next.type == KeyPress
-                            && next.xkey.time == evt.xkey.time
-                            && next.xkey.keycode == evt.xkey.keycode )
-                        {
-                            XNextEvent(__display,&next);
-                            continue;
-                        }
-                    }
-                    
-                    KeySym sym = XLookupKeysym(&evt.xkey, 0);
-                    Keyboard::Key key = getKey(sym);
-                    gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_RELEASE, key);
+        // Setup select for message handling (to allow non-blocking)
+        int x11_fd = ConnectionNumber(__display);
 
-                    if (key == Keyboard::KEY_SHIFT)
-                        shiftDown = false;
-                }
-                break;
+        pollfd xpolls[1];
+        xpolls[0].fd = x11_fd;
+        xpolls[0].events = POLLIN|POLLPRI;
 
-            case ButtonPress:
-                {
-                    gameplay::Mouse::MouseEvent mouseEvt;
-                    switch (evt.xbutton.button)
-                    {
-                        case 1:
-                            mouseEvt = gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON;
-                            break;
-                        case 2:
-                            mouseEvt = gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON;
-                            break;
-                        case 3:
-                            mouseEvt = gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON;
-                            break;
-                        case 4:
-                        case 5:
-                            gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL,
-                                                                   evt.xbutton.x, evt.xbutton.y,
-                                                                   evt.xbutton.button == Button4 ? 1 : -1);
-                            break;
-                        default:
-                            break;
-                    }
-                    if (!gameplay::Platform::mouseEventInternal(mouseEvt, evt.xbutton.x, evt.xbutton.y, 0))
-                    {
-                        gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, evt.xbutton.x, evt.xbutton.y, 0);
-                    }
-                }
-                break;
+        // Message loop.
+        while (true)
+        {
+            poll( xpolls, 1, 16 );
+            // handle all pending events in one block
+            while (XPending(__display))
+            {
+                XNextEvent(__display, &evt);
 
-            case ButtonRelease:
+                switch (evt.type)
                 {
-                    gameplay::Mouse::MouseEvent mouseEvt;
-                    switch (evt.xbutton.button)
-                    {
-                        case 1:
-                            mouseEvt = gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON;
-                            break;
-                        case 2:
-                            mouseEvt = gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON;
-                            break;
-                        case 3:
-                            mouseEvt = gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON;
-                            break;
-                        default:
-                            break;
-                    }
-                    if (!gameplay::Platform::mouseEventInternal(mouseEvt, evt.xbutton.x, evt.xbutton.y, 0))
-                    {
-                        gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, evt.xbutton.x, evt.xbutton.y, 0);
-                    }
-                }
-                break;
-        
-            case MotionNotify:
-                {
-                    int x = evt.xmotion.x;
-                    int y = evt.xmotion.y;
+                    case ClientMessage:
+                        {
+                            // Handle destroy window message correctly
+                            if (evt.xclient.data.l[0] == __atomWmDeleteWindow)
+                            {
+                                _game->exit();
+                            }
+                        }
+                        break;
+                    case DestroyNotify :
+                        {
+                            cleanupX11();
+                            exit(0);
+                        }
+                        break;
 
-                    if (__mouseCaptured)
-                    {
-                        if (x == __mouseCapturePointX && y == __mouseCapturePointY)
+                    case Expose:
                         {
-                            // Discard the first MotionNotify following capture
-                            // since it contains bogus x,y data.
-                            break;
+                            updateWindowSize();
                         }
+                        break;
 
-                        // Convert to deltas
-                        x -= __mouseCapturePointX;
-                        y -= __mouseCapturePointY;
+                    case KeyPress:
+                        {
+                            KeySym sym = XLookupKeysym(&evt.xkey, (evt.xkey.state & shiftDown) ? 1 : 0);
 
-                        // Warp mouse back to center of screen.
-                        XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
-                    }
 
-                    if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
-                    {
-                        if (evt.xmotion.state & Button1Mask)
+                            //TempSym needed because XConvertCase operates on two keysyms: One lower and the other upper, we are only interested in the upper case
+                            KeySym tempSym;
+                            if (capsOn && !shiftDown)
+                                XConvertCase(sym,  &tempSym, &sym);
+
+                            Keyboard::Key key = getKey(sym);
+                            gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, key);
+
+                            if (key == Keyboard::KEY_CAPS_LOCK)
+                                capsOn = !capsOn;
+                            if (key == Keyboard::KEY_SHIFT)
+                                shiftDown = true;
+
+                            if (int character = getUnicode(key))
+                                gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, character);
+
+                        }
+                        break;
+
+                    case KeyRelease:
                         {
-                            gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
+                            //detect and drop repeating keystrokes (no other way to do this using the event interface)
+                            XEvent next;
+                            if ( XPending(__display) )
+                            {
+                                XPeekEvent(__display,&next);
+                                if ( next.type == KeyPress
+                                        && next.xkey.time == evt.xkey.time
+                                        && next.xkey.keycode == evt.xkey.keycode )
+                                {
+                                    XNextEvent(__display,&next);
+                                    continue;
+                                }
+                            }
+
+                            KeySym sym = XLookupKeysym(&evt.xkey, 0);
+                            Keyboard::Key key = getKey(sym);
+                            gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_RELEASE, key);
+
+                            if (key == Keyboard::KEY_SHIFT)
+                                shiftDown = false;
                         }
-                        else if (evt.xmotion.state & Button3Mask)
+                        break;
+
+                    case ButtonPress:
                         {
-                            // Update the pitch and roll by adding the scaled deltas.
-                            __roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
-                            __pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
+                            gameplay::Mouse::MouseEvent mouseEvt;
+                            switch (evt.xbutton.button)
+                            {
+                                case 1:
+                                    mouseEvt = gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON;
+                                    break;
+                                case 2:
+                                    mouseEvt = gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON;
+                                    break;
+                                case 3:
+                                    mouseEvt = gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON;
+                                    break;
+                                case 4:
+                                case 5:
+                                    gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL,
+                                            evt.xbutton.x, evt.xbutton.y,
+                                            evt.xbutton.button == Button4 ? 1 : -1);
+                                    break;
+                                default:
+                                    break;
+                            }
+                            if (!gameplay::Platform::mouseEventInternal(mouseEvt, evt.xbutton.x, evt.xbutton.y, 0))
+                            {
+                                gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, evt.xbutton.x, evt.xbutton.y, 0);
+                            }
+                        }
+                        break;
 
-                            // Clamp the values to the valid range.
-                            __roll = max(min(__roll, 90.0f), -90.0f);
-                            __pitch = max(min(__pitch, 90.0f), -90.0f);
+                    case ButtonRelease:
+                        {
+                            gameplay::Mouse::MouseEvent mouseEvt;
+                            switch (evt.xbutton.button)
+                            {
+                                case 1:
+                                    mouseEvt = gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON;
+                                    break;
+                                case 2:
+                                    mouseEvt = gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON;
+                                    break;
+                                case 3:
+                                    mouseEvt = gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON;
+                                    break;
+                                default:
+                                    break;
+                            }
+                            if (!gameplay::Platform::mouseEventInternal(mouseEvt, evt.xbutton.x, evt.xbutton.y, 0))
+                            {
+                                gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, evt.xbutton.x, evt.xbutton.y, 0);
+                            }
+                        }
+                        break;
 
-                            // Update the last X/Y values.
-                            lx = x;
-                            ly = y;
+                    case MotionNotify:
+                        {
+                            int x = evt.xmotion.x;
+                            int y = evt.xmotion.y;
+
+                            if (__mouseCaptured)
+                            {
+                                if (x == __mouseCapturePointX && y == __mouseCapturePointY)
+                                {
+                                    // Discard the first MotionNotify following capture
+                                    // since it contains bogus x,y data.
+                                    break;
+                                }
+
+                                // Convert to deltas
+                                x -= __mouseCapturePointX;
+                                y -= __mouseCapturePointY;
+
+                                // Warp mouse back to center of screen.
+                                XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+                            }
+
+                            if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
+                            {
+                                if (evt.xmotion.state & Button1Mask)
+                                {
+                                    gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
+                                }
+                                else if (evt.xmotion.state & Button3Mask)
+                                {
+                                    // Update the pitch and roll by adding the scaled deltas.
+                                    __roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
+                                    __pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
+
+                                    // Clamp the values to the valid range.
+                                    __roll = max(min(__roll, 90.0f), -90.0f);
+                                    __pitch = max(min(__pitch, 90.0f), -90.0f);
+
+                                    // Update the last X/Y values.
+                                    lx = x;
+                                    ly = y;
+                                }
+                            }
                         }
-                    }
+                        break;
+
+                    default:
+                        break;
                 }
-                break;
-            
-            default:
-                break;
             }
-        }
 
-        if (_game)
-        {
-            // Game state will be uninitialized if game was closed through Game::exit()
-            if (_game->getState() == Game::UNINITIALIZED)
-                break;
-            
-            _game->frame();
-        }
-
-        glXSwapBuffers(__display, __window);
-    }
+            gamepadHandlingLoop();
 
-    cleanupX11();
+            if (_game)
+            {
+                // Game state will be uninitialized if game was closed through Game::exit()
+                if (_game->getState() == Game::UNINITIALIZED)
+                    break;
 
-    return 0;
-}
-    
-void Platform::signalShutdown()
-{
-    // nothing to do  
-}
+                _game->frame();
+            }
 
-bool Platform::canExit()
-{
-    return true;
-}
-    
-unsigned int Platform::getDisplayWidth()
-{
-    return __windowSize[0];
-}
+            glXSwapBuffers(__display, __window);
+        }
 
-unsigned int Platform::getDisplayHeight()
-{
-    return __windowSize[1];
-}
+        cleanupX11();
 
-double Platform::getAbsoluteTime()
-{
+        return 0;
+    }
 
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    double now = timespec2millis(&__timespec);
-    __timeAbsolute = now - __timeStart;
+    void Platform::signalShutdown()
+    {
+    }
 
-    return __timeAbsolute;
-}
+    bool Platform::canExit()
+    {
+        return true;
+    }
 
-void Platform::setAbsoluteTime(double time)
-{
-    __timeAbsolute = time;
-}
+    unsigned int Platform::getDisplayWidth()
+    {
+        return __windowSize[0];
+    }
 
-bool Platform::isVsync()
-{
-    return __vsync;
-}
+    unsigned int Platform::getDisplayHeight()
+    {
+        return __windowSize[1];
+    }
 
-void Platform::setVsync(bool enable)
-{
-    if (glXSwapIntervalEXT)
-        glXSwapIntervalEXT(__display, __window, __vsync ? 1 : 0);
-    else if(glXSwapIntervalMESA)
-        glXSwapIntervalMESA(__vsync ? 1 : 0);
+    double Platform::getAbsoluteTime()
+    {
 
-    __vsync = enable;
-}
+        clock_gettime(CLOCK_REALTIME, &__timespec);
+        double now = timespec2millis(&__timespec);
+        __timeAbsolute = now - __timeStart;
 
-void Platform::swapBuffers()
-{
-    glXSwapBuffers(__display, __window);
-}
+        return __timeAbsolute;
+    }
 
-void Platform::sleep(long ms)
-{
-    usleep(ms * 1000);
-}
+    void Platform::setAbsoluteTime(double time)
+    {
+        __timeAbsolute = time;
+    }
 
-void Platform::setMultiTouch(bool enabled)
-{
-    // not supported
-}
+    bool Platform::isVsync()
+    {
+        return __vsync;
+    }
 
-bool Platform::isMultiTouch()
-{
-    false;
-}
+    void Platform::setVsync(bool enable)
+    {
+        if (glXSwapIntervalEXT)
+            glXSwapIntervalEXT(__display, __window, __vsync ? 1 : 0);
+        else if(glXSwapIntervalMESA)
+            glXSwapIntervalMESA(__vsync ? 1 : 0);
 
-void Platform::getAccelerometerValues(float* pitch, float* roll)
-{
-    GP_ASSERT(pitch);
-    GP_ASSERT(roll);
+        __vsync = enable;
+    }
 
-    *pitch = __pitch;
-    *roll = __roll;
-}
+    void Platform::swapBuffers()
+    {
+        glXSwapBuffers(__display, __window);
+    }
 
-bool Platform::hasMouse()
-{
-    return true;
-}
+    void Platform::sleep(long ms)
+    {
+        usleep(ms * 1000);
+    }
 
-void Platform::setMouseCaptured(bool captured)
-{
-    if (captured != __mouseCaptured)
+    void Platform::setMultiSampling(bool enabled)
     {
-        if (captured)
+        if (enabled == __multiSampling)
         {
-            // Hide the cursor and warp it to the center of the screen
-            __mouseCapturePointX = getDisplayWidth() / 2;
-            __mouseCapturePointY = getDisplayHeight() / 2;
-
-            setCursorVisible(false);
-            XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+            return;
         }
-        else
+        
+            //todo
+            
+            __multiSampling = enabled;
+    }
+    
+        bool Platform::isMultiSampling()
         {
-            // Restore cursor
-            XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
-            setCursorVisible(true);
+            return __multiSampling;
         }
 
-        __mouseCaptured = captured;
+    void Platform::setMultiTouch(bool enabled)
+    {
+        // not supported
     }
-}
 
-bool Platform::isMouseCaptured()
-{
-    return __mouseCaptured;
-}
+    bool Platform::isMultiTouch()
+    {
+        false;
+    }
 
-void Platform::setCursorVisible(bool visible)
-{
-    if (visible != __cursorVisible)
+    void Platform::getAccelerometerValues(float* pitch, float* roll)
+    {
+        GP_ASSERT(pitch);
+        GP_ASSERT(roll);
+
+        *pitch = __pitch;
+        *roll = __roll;
+    }
+
+    void Platform::getArguments(int* argc, char*** argv)
     {
-        if (visible)
+        if (argc)
+            *argc = __argc;
+        if (argv)
+            *argv = __argv;
+    }
+
+    bool Platform::hasMouse()
+    {
+        return true;
+    }
+
+    void Platform::setMouseCaptured(bool captured)
+    {
+        if (captured != __mouseCaptured)
         {
-            Cursor invisibleCursor;
-            Pixmap bitmapNoData;
-            XColor black;
-            static char noData[] = {0, 0, 0, 0, 0, 0, 0, 0};
-            black.red = black.green = black.blue = 0;
-            bitmapNoData = XCreateBitmapFromData(__display, __window, noData, 8, 8);
-            invisibleCursor = XCreatePixmapCursor(__display, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
-
-            XDefineCursor(__display, __window, invisibleCursor);
-            XFreeCursor(__display, invisibleCursor);
-            XFreePixmap(__display, bitmapNoData);
+            if (captured)
+            {
+                // Hide the cursor and warp it to the center of the screen
+                __mouseCapturePointX = getDisplayWidth() / 2;
+                __mouseCapturePointY = getDisplayHeight() / 2;
+
+                setCursorVisible(false);
+                XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+            }
+            else
+            {
+                // Restore cursor
+                XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+                setCursorVisible(true);
+            }
+
+            __mouseCaptured = captured;
         }
-        else
+    }
+
+    bool Platform::isMouseCaptured()
+    {
+        return __mouseCaptured;
+    }
+
+    void Platform::setCursorVisible(bool visible)
+    {
+        if (visible != __cursorVisible)
         {
-            XUndefineCursor(__display, __window);
+            if (visible)
+            {
+                Cursor invisibleCursor;
+                Pixmap bitmapNoData;
+                XColor black;
+                static char noData[] = {0, 0, 0, 0, 0, 0, 0, 0};
+                black.red = black.green = black.blue = 0;
+                bitmapNoData = XCreateBitmapFromData(__display, __window, noData, 8, 8);
+                invisibleCursor = XCreatePixmapCursor(__display, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
+
+                XDefineCursor(__display, __window, invisibleCursor);
+                XFreeCursor(__display, invisibleCursor);
+                XFreePixmap(__display, bitmapNoData);
+            }
+            else
+            {
+                XUndefineCursor(__display, __window);
+            }
+            XFlush(__display);
+            __cursorVisible = visible;
         }
-        XFlush(__display);
-        __cursorVisible = visible;
     }
-}
 
-bool Platform::isCursorVisible()
-{
-    return __cursorVisible;
-}
+    bool Platform::isCursorVisible()
+    {
+        return __cursorVisible;
+    }
 
-void Platform::displayKeyboard(bool display)
-{
-    // not supported
-}
+    void Platform::displayKeyboard(bool display)
+    {
+        // not supported
+    }
 
-void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    if (!Form::touchEventInternal(evt, x, y, contactIndex))
+    void Platform::shutdownInternal()
     {
-        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-        Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
+        closeAllGamepads();
+        Game::getInstance()->shutdown();
     }
-}
 
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    if (!Form::keyEventInternal(evt, key))
+    bool Platform::isGestureSupported(Gesture::GestureEvent evt)
     {
-        Game::getInstance()->keyEvent(evt, key);
-        Game::getInstance()->getScriptController()->keyEvent(evt, key);
+        return false;
     }
-}
 
-bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
-{
-    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    void Platform::registerGesture(Gesture::GestureEvent evt)
     {
-        return true;
     }
-    else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
+
+    void Platform::unregisterGesture(Gesture::GestureEvent evt)
     {
-        return true;
     }
-    else
+
+    bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
     {
-        return Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
+        return false;
     }
-}
-
-void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-                                             unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
-{
-    Gamepad::add(handle, buttonCount, joystickCount, triggerCount, vendorId, productId, vendorString, productString);
-}
 
-void Platform::gamepadEventDisconnectedInternal(GamepadHandle handle)
-{
-    Gamepad::remove(handle);
-}
+    void Platform::pollGamepadState(Gamepad* gamepad)
+    {
+        GP_ASSERT(gamepad);
 
-void Platform::shutdownInternal()
-{
-    Game::getInstance()->shutdown();
-}
+        struct js_event jevent;
+        const GamepadInfoEntry& gpInfo = getGamepadMappedInfo(gamepad->_handle);
 
-bool Platform::isGestureSupported(Gesture::GestureEvent evt)
-{
-    return false;
-}
+        while (read(gamepad->_handle, &jevent, sizeof(struct js_event)) > 0)
+        {
+            switch (jevent.type)
+            {
+                case JS_EVENT_BUTTON:
+                case JS_EVENT_BUTTON | JS_EVENT_INIT:
+                    {
+                        long curMappingIndex = -1;
+                        if(tryGetGamepadMappedButton(gpInfo, jevent.number, curMappingIndex))
+                        {
+                            unsigned int buttons = 0;
+                            if (jevent.value)
+                                buttons |= (1 << curMappingIndex);
+                            else
+                                buttons &= ~(1 << curMappingIndex);
+                            gamepad->setButtons(buttons);
+                        }
+                        break;
+                    }
+                case JS_EVENT_AXIS:
+                case JS_EVENT_AXIS | JS_EVENT_INIT:
+                    {
+                        if(jevent.number < gpInfo.numberOfAxes)
+                        {
+                            const GamepadJoystickAxisInfo* jsInfo = tryGetGamepadMappedAxisInfo(gpInfo,jevent.number);
+                            if(jsInfo)
+                            {
+                                float val = normalizeJoystickAxis(jevent.value,jsInfo->deadZone,jsInfo->mapFunc == ZERO_TO_POS);
+                                if(!(jsInfo->flags & GP_AXIS_SKIP))
+                                {
+                                    if((jsInfo->flags & GP_AXIS_IS_NEG))
+                                        val = -1.0f * val;
+
+                                    bool not_js_axis = false;
+                                    if((jsInfo->flags & GP_AXIS_IS_DPAD))
+                                    {
+                                        unsigned int buttons = 0;
+                                        if(jevent.value != 0)
+                                            buttons |= (1 << (jevent.value > 0 ? jsInfo->mappedPosArg : jsInfo->mappedNegArg));
+                                        else
+                                        {
+                                            buttons &= ~(1 << jsInfo->mappedPosArg);
+                                            buttons &= ~(1 << jsInfo->mappedNegArg);
+                                        }
+                                        gamepad->setButtons(buttons);
+                                        not_js_axis = true;
+                                    }
+                                    if((jsInfo->flags & GP_AXIS_IS_TRIGGER))
+                                    {
+                                        gamepad->setTriggerValue(jsInfo->mappedPosArg, val);
+                                        not_js_axis = true;
+                                    }
+
+                                    if(!not_js_axis)
+                                    {
+                                        Vector2 jsVals;
+                                        gamepad->getJoystickValues(jsInfo->joystickIndex,&jsVals);
+                                        if(jsInfo->flags & GP_AXIS_IS_XAXIS)
+                                            jsVals.x = val;
+                                        else
+                                            jsVals.y = val;
+                                        gamepad->setJoystickValue(jsInfo->joystickIndex,jsVals.x,jsVals.y);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    break;
 
-void Platform::registerGesture(Gesture::GestureEvent evt)
-{
-}
+                default: 
+                    GP_WARN("unhandled gamepad event: %x\n", jevent.type);
+            }
+        }
+        if(errno == ENODEV)
+        {
+            unregisterGamepad(gamepad->_handle);
+            gamepadEventDisconnectedInternal(gamepad->_handle);
+        }
 
-void Platform::unregisterGesture(Gesture::GestureEvent evt)
-{
-}
-    
-bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
-{
-    return false;
-}
+    }
 
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-}
+    bool Platform::launchURL(const char* url)
+    {
+        if (url == NULL || *url == '\0')
+            return false;
 
-bool Platform::launchURL(const char* url)
-{
-    if (url == NULL || *url == '\0')
-        return false;
+        int len = strlen(url);
 
-    int len = strlen(url);
-    
-    char* cmd = new char[11 + len];
-    sprintf(cmd, "xdg-open %s", url);
-    int r = system(cmd);
-    SAFE_DELETE_ARRAY(cmd);
-    
-    return (r == 0);
-}
+        char* cmd = new char[11 + len];
+        sprintf(cmd, "xdg-open %s", url);
+        int r = system(cmd);
+        SAFE_DELETE_ARRAY(cmd);
+
+        return (r == 0);
+    }
 
 }
 

+ 2286 - 2057
gameplay/src/PlatformMacOSX.mm

@@ -1,2057 +1,2286 @@
-#ifdef __APPLE__
-
-#include "Base.h"
-#include "Platform.h"
-#include "FileSystem.h"
-#include "Game.h"
-#include "Form.h"
-#include "ScriptController.h"
-#include <unistd.h>
-#include <IOKit/hid/IOHIDLib.h>
-#import <Cocoa/Cocoa.h>
-#import <QuartzCore/CVDisplayLink.h>
-#import <OpenGL/OpenGL.h>
-#import <mach/mach_time.h>
-#import <Foundation/Foundation.h>
-
-// These should probably be moved to a platform common file
-#define SONY_USB_VENDOR_ID          0x54c
-#define SONY_USB_PS3_PRODUCT_ID     0x268
-
-
-using namespace std;
-using namespace gameplay;
-
-@class View;
-@class HIDGamepad;
-
-// Default to 720p
-static int __width = 1280;
-static int __height = 720;
-
-static float ACCELEROMETER_FACTOR_X = 90.0f / __width;
-static float ACCELEROMETER_FACTOR_Y = 90.0f / __height;
-
-static double __timeStart;
-static double __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 __otherMouseDown = false;
-static bool __shiftDown = false;
-static char* __title = NULL;
-static bool __fullscreen = false;
-static void* __attachToWindow = NULL;
-static bool __mouseCaptured = false;
-static bool __mouseCapturedFirstPass = false;
-static CGPoint __mouseCapturePoint;
-static bool __cursorVisible = true;
-static View* __view = NULL;
-
-static NSMutableDictionary *__activeGamepads = NULL;
-static NSMutableArray *__gamepads = NULL;
-static IOHIDManagerRef __hidManagerRef = NULL;
-
-// Gamepad Helper Function
-HIDGamepad *gamepadForLocationID(NSNumber *locationID);
-HIDGamepad *gamepadForLocationIDValue(unsigned int locationIDValue);
-HIDGamepad *gamepadForGameHandle(int gameHandle);
-
-
-// IOHid Helper Functions
-CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage);
-CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
-int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
-
-// IOHid Callbacks
-static void hidDeviceDiscoveredCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
-static void hidDeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
-static void hidDeviceValueAvailableCallback(void *inContext, IOReturn inResult,  void *inSender);
-
-
-
-double getMachTimeInMilliseconds()
-{
-    static const double 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
-    GP_ASSERT(s_timebase_info.denom);
-    return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
-}
-
-
-@interface HIDGamepadAxis : NSObject
-{
-    IOHIDElementRef e;
-    CFIndex v;
-    CFIndex logMin;
-    CFIndex logMax;
-}
-+ gamepadAxisWithAxisElement:(IOHIDElementRef)element;
-- initWithAxisElement:(IOHIDElementRef)element;
-- (IOHIDElementRef)element;
-- (IOHIDElementCookie)cookie;
-- (uint32_t)usage;
-- (uint32_t)usagePage;
-- (CFIndex)logicalMinimum;
-- (CFIndex)logicalMaximum;
-
-- (float)calibratedValue;
-- (CFIndex)value;
-- (void)setValue:(CFIndex)value;
-@end
-@implementation HIDGamepadAxis
-+ gamepadAxisWithAxisElement:(IOHIDElementRef)element
-{
-    return [[[[self class] alloc] initWithAxisElement:element] autorelease];
-}
-- initWithAxisElement:(IOHIDElementRef)element
-{
-    if((self = [super init]))
-    {
-        e = (IOHIDElementRef)CFRetain(element);
-    }
-    return self;
-}
-- (void)dealloc
-{
-    CFRelease(e);
-    [super dealloc];
-}
-- (IOHIDElementRef)element
-{
-    return e;
-}
-- (IOHIDElementCookie)cookie
-{
-    return IOHIDElementGetCookie(e);
-}
-- (bool)isHatSwitch {
-    return (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch);
-}
-- (uint32_t)usage
-{
-    return IOHIDElementGetUsage(e);
-}
-- (uint32_t)usagePage
-{
-    return IOHIDElementGetUsagePage(e);
-}
-- (CFIndex)logicalMinimum
-{
-    return IOHIDElementGetLogicalMin(e);    
-}
-- (CFIndex)logicalMaximum
-{
-    return IOHIDElementGetLogicalMax(e);
-}
-- (float)calibratedValue
-{
-    float cmax = 2.0f;
-    float cmin = 0.0f;
-    return ((((v - [self logicalMinimum]) * (cmax - cmin)) / ([self logicalMaximum] - [self logicalMinimum])) + cmin - 1.0f);    
-}
-- (CFIndex)value
-{
-    return v;
-}
-- (void)setValue:(CFIndex)value
-{
-    v = value;
-}
-@end
-
-@interface HIDGamepadButton : NSObject
-{
-    IOHIDElementRef e;
-    IOHIDElementRef te;
-    bool state;
-    int triggerValue;
-}
-+ gamepadButtonWithButtonElement:(IOHIDElementRef)element;
-- initWithButtonElement:(IOHIDElementRef)element;
-- (void)setTriggerElement:(IOHIDElementRef)element;
-- (IOHIDElementRef)element;
-- (IOHIDElementCookie)cookie;
-- (IOHIDElementRef)triggerElement;
-- (IOHIDElementCookie)triggerCookie;
-
-- (bool)isTriggerButton;
-- (uint32_t)usage;
-- (uint32_t)usagePage;
-- (int)stateValue;
-- (float)calibratedStateValue;
-- (void)setStateValue:(int)value;
-- (bool)state;
-- (void)setState:(bool)state;
-@end
-@implementation HIDGamepadButton
-+ gamepadButtonWithButtonElement:(IOHIDElementRef)element
-{
-    return [[[[self class] alloc] initWithButtonElement:element] autorelease];
-}
-- initWithButtonElement:(IOHIDElementRef)element
-{
-    if((self = [super init]))
-    {
-        e = (IOHIDElementRef)CFRetain(element);
-        te = NULL;
-        state = false;
-    }
-    return self;
-}
-- (void)dealloc
-{
-    CFRelease(e);
-    if(te != NULL) CFRelease(te);
-    [super dealloc];
-}
-- (void)setTriggerElement:(IOHIDElementRef)element {
-    if(te)
-    {
-        CFRelease(te);
-        te = NULL;
-    }
-    if(element)
-    {
-        te = (IOHIDElementRef)CFRetain(element);
-    }
-}
-- (IOHIDElementRef)element
-{
-    return e;
-}
-- (IOHIDElementCookie)cookie
-{
-    return IOHIDElementGetCookie(e);
-}
-- (IOHIDElementRef)triggerElement
-{
-    return te;
-}
-- (IOHIDElementCookie)triggerCookie
-{
-    return IOHIDElementGetCookie(te);
-}
-- (bool)isTriggerButton
-{
-    return (te != NULL);
-}
-- (uint32_t)usage
-{
-    return IOHIDElementGetUsage(e);
-}
-- (uint32_t)usagePage
-{
-    return IOHIDElementGetUsagePage(e);
-}
-- (void)setStateValue:(int)value {
-    triggerValue = value;
-}
-- (int)stateValue
-{
-    return triggerValue;
-}
-- (float)calibratedStateValue
-{
-    return (float)triggerValue / 255.0f;
-}
-- (bool)state
-{
-    return state;
-}
-- (void)setState:(bool)s
-{
-    state = s;
-}
-@end
-
-@interface HIDGamepad : NSObject
-{
-    IOHIDDeviceRef hidDeviceRef;
-    IOHIDQueueRef queueRef;
-    NSMutableArray *buttons;
-    NSMutableArray *triggerButtons;
-    NSMutableArray *axes;
-}
-@property (assign) IOHIDDeviceRef hidDeviceRef;
-@property (assign) IOHIDQueueRef queueRef;
-@property (retain) NSMutableArray *buttons;
-@property (retain) NSMutableArray *triggerButtons;
-@property (retain) NSMutableArray *axes;
-
-- initWithDevice:(IOHIDDeviceRef)rawDevice;
-- (IOHIDDeviceRef)rawDevice;
-- (NSNumber*)locationID;
-
-- (void)initializeGamepadElements;
-- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie;
-
-- (bool)startListening;
-- (void)stopListening;
-
-- (NSString *)identifierName;
-- (NSString *)productName;
-- (NSString *)manufacturerName;
-- (NSString *)serialNumber;
-- (int)vendorID;
-- (int)productID;
-
-- (NSUInteger)numberOfAxes;
-- (NSUInteger)numberOfSticks;
-- (NSUInteger)numberOfButtons;
-- (NSUInteger)numberOfTriggerButtons;
-- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index;
-- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index;
-- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index;
-@end
-
-@implementation HIDGamepad
-
-@synthesize hidDeviceRef;
-@synthesize queueRef;
-@synthesize buttons;
-@synthesize triggerButtons;
-@synthesize axes;
-
-- initWithDevice:(IOHIDDeviceRef)rawDevice
-{
-    if((self = [super init]))
-    {
-        [self setButtons:[NSMutableArray array]];
-        [self setTriggerButtons:[NSMutableArray array]];
-        [self setAxes:[NSMutableArray array]];
-
-        CFRetain(rawDevice);
-        IOHIDQueueRef queue = IOHIDQueueCreate(CFAllocatorGetDefault(), rawDevice, 10, kIOHIDOptionsTypeNone);
-        [self setHidDeviceRef:rawDevice];
-        [self setQueueRef:queue];
-        
-        [self initializeGamepadElements];
-        [self startListening];
-    }
-    return self;
-}
-- (void)dealloc
-{
-    [self stopListening];
-    
-    CFRelease([self rawDevice]);
-    CFRelease([self queueRef]);
-    [self setQueueRef:NULL];
-    [self setHidDeviceRef:NULL];
-    
-    [self setButtons:NULL];
-    [self setTriggerButtons:NULL];
-    [self setAxes:NULL];
-    [super dealloc];
-}
-- (IOHIDDeviceRef)rawDevice
-{
-    return [self hidDeviceRef];
-}
-- (NSNumber*)locationID
-{
-    return (NSNumber*)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDLocationIDKey));
-}
-
-- (void)initializeGamepadElements
-{
-    CFArrayRef elements = IOHIDDeviceCopyMatchingElements([self rawDevice], NULL, kIOHIDOptionsTypeNone);
-    for(int i = 0; i < CFArrayGetCount(elements); i++)
-    {
-        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
-        IOHIDElementType type = IOHIDElementGetType(hidElement);
-
-        if (type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis)
-        {
-            uint32_t pageUsage = IOHIDElementGetUsage(hidElement);
-            IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
-
-            switch(pageUsage)
-            {
-                case kHIDUsage_GD_X:
-                case kHIDUsage_GD_Y:
-                case kHIDUsage_GD_Z:
-                case kHIDUsage_GD_Rx:
-                case kHIDUsage_GD_Ry:
-                case kHIDUsage_GD_Rz:
-                {
-                    HIDGamepadAxis *axis = [HIDGamepadAxis gamepadAxisWithAxisElement:hidElement];
-                    [[self axes] addObject:axis];
-                }
-                    break;
-                default:
-                    // Ignore the pointers
-                    // Note: Some of the pointers are for the 6-axis accelerometer in a PS3 controller
-                    // Note: L2/R2 triggers are at cookie 39 and 40 base 10 tied to 9 and 10 button elements
-                    break;
-            }
-
-        }
-        if(type == kIOHIDElementTypeInput_Button)
-        {
-            HIDGamepadButton *button = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
-            [[self buttons] addObject:button];
-        }
-    }
-    // Go back and get proprietary information (e.g. triggers) and associate with appropriate values
-    // Example for other trigger buttons
-    uint32_t vendorID = [self vendorID];
-    uint32_t productID = [self productID];
-    for(int i = 0; i < CFArrayGetCount(elements); i++)
-    {
-        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
-        IOHIDElementType type = IOHIDElementGetType(hidElement);
-        IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
-        
-        // Gamepad specific code
-        // Not sure if there is a better way to associate buttons and misc hid elements :/
-        if(vendorID == SONY_USB_VENDOR_ID && productID == SONY_USB_PS3_PRODUCT_ID)
-        {
-            if((unsigned long)cookie == 39)
-            {
-                //[self buttonAtIndex:8]; 
-                HIDGamepadButton *leftTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)9];
-                if(leftTriggerButton)
-                {
-                    [leftTriggerButton setTriggerElement:hidElement];
-                    [[self triggerButtons] addObject:leftTriggerButton];
-                    // I would have thought this would work but it seems to mess things up, even after re-mapping the buttons.
-                    //[[self buttons] removeObject:leftTriggerButton];
-                }
-            }
-            if((unsigned long)cookie == 40)
-            {
-                //[self buttonAtIndex:9];
-                HIDGamepadButton *rightTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)10];
-                if(rightTriggerButton)
-                {
-                    [rightTriggerButton setTriggerElement:hidElement];
-                    [[self triggerButtons] addObject:rightTriggerButton];
-                    //[[self buttons] removeObject:rightTriggerButton];
-                }
-            }
-        }
-    }
-}
-
-- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie {
-    for(HIDGamepadButton *b in [self buttons]) {
-        if([b cookie] == cookie)
-            return b;
-    }
-    return NULL;
-}
-
-- (bool)startListening
-{
-    IOReturn kr = IOHIDDeviceOpen([self hidDeviceRef], kIOHIDOptionsTypeNone);
-    if(kr != 0) {
-        return false;
-    }
-    IOHIDDeviceScheduleWithRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    
-    IOHIDQueueStart([self queueRef]);
-    IOHIDQueueRegisterValueAvailableCallback([self queueRef], hidDeviceValueAvailableCallback, self);
-    
-    CFArrayRef elements = (CFArrayRef)[self watchedElements];
-    for(int i = 0; i < CFArrayGetCount(elements); i++)
-    {
-        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
-        IOHIDQueueAddElement([self queueRef], hidElement);
-    }
-    
-    IOHIDQueueScheduleWithRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    
-    return true;
-}
-- (void)stopListening
-{
-    IOHIDQueueUnscheduleFromRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDQueueStop([self queueRef]);
-    
-    IOHIDDeviceUnscheduleFromRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDDeviceClose([self hidDeviceRef], kIOHIDOptionsTypeNone);
-}
-
-- (NSString *)identifierName
-{
-    NSString *idName = NULL;
-    if(idName == NULL) idName = [self productName];
-    if(idName == NULL) idName = [self manufacturerName];
-    if(idName == NULL) idName = [self serialNumber];
-    if(idName == NULL) idName = [NSString stringWithFormat:@"%d-%d", [self vendorID], [self productID]];
-    return idName;
-}
-
-- (NSString *)productName {
-    CFStringRef productName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDProductKey));
-    if(productName == NULL || CFGetTypeID(productName) != CFStringGetTypeID()) {
-        return NULL;
-    }
-    return (NSString*)productName;
-}
-- (NSString *)manufacturerName {
-    CFStringRef manufacturerName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDManufacturerKey));
-    if(manufacturerName == NULL || CFGetTypeID(manufacturerName) != CFStringGetTypeID()) {
-        return NULL;
-    }
-    return (NSString*)manufacturerName;
-}
-- (NSString *)serialNumber {
-    CFStringRef serialNumber = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDSerialNumberKey));
-    if(serialNumber == NULL || CFGetTypeID(serialNumber) != CFStringGetTypeID()) {
-        return NULL;
-    }
-    return (NSString*)serialNumber;
-}
-
-
-- (int)vendorID
-{
-    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVendorIDKey));
-}
-- (int)productID
-{
-    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDProductIDKey));
-}
-
-
-- (NSUInteger)numberOfAxes
-{
-    return [[self axes] count];
-}
-- (NSUInteger)numberOfSticks
-{
-    return ([[self axes] count] / 2);
-}
-- (NSUInteger)numberOfButtons
-{
-    return [[self buttons] count];
-}
-- (NSUInteger)numberOfTriggerButtons
-{
-    return [[self triggerButtons] count];
-}
-
-- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index
-{
-    HIDGamepadButton *b = NULL;
-    if(index < [[self triggerButtons] count])
-    {
-        b = [[self triggerButtons] objectAtIndex:index];
-    }
-    return b;
-}
-
-- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index
-{
-    HIDGamepadAxis *a = NULL;
-    if(index < [[self axes] count])
-    {
-        a = [[self axes] objectAtIndex:index];
-    }
-    return a;
-}
-- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index
-{
-    HIDGamepadButton *b = NULL;
-    if(index < [[self buttons] count])
-    {
-        b = [[self buttons] objectAtIndex:index];
-    }
-    return b;
-}
-- (NSArray*)watchedElements
-{
-    NSMutableArray *r = [NSMutableArray array];
-    for(HIDGamepadButton *b in [self buttons])
-    {
-        [r addObject:(id)[b element]];
-    }
-    for(HIDGamepadAxis *a in [self axes])
-    {
-        [r addObject:(id)[a element]];
-    }
-    for(HIDGamepadButton* t in [self triggerButtons])
-    {
-        [r addObject:(id)[t triggerElement]];
-    }
-    return [NSArray arrayWithArray:r];
-}
-
-- (void)hidValueAvailable:(IOHIDValueRef)value
-{
-    IOHIDElementRef element = IOHIDValueGetElement(value);
-    IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
-    
-    if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
-    CFIndex integerValue = IOHIDValueGetIntegerValue(value);
-    
-    for(HIDGamepadAxis *a in [self axes])
-    {
-        if([a cookie] == cookie)
-        {
-            [a setValue:integerValue];
-        }
-    }
-    
-    for(HIDGamepadButton *b in [self buttons])
-    {
-        if([b cookie] == cookie)
-        {
-            [b setState:(bool)integerValue];
-            break;
-        }
-    }
-    
-    for(HIDGamepadButton *b in [self triggerButtons])
-    {
-        if([b triggerCookie] == cookie)
-        {
-            [b setStateValue:integerValue];
-            break;
-        }
-    }
-
-}
-
-
-@end
-
-
-
-
-@interface View : NSOpenGLView <NSWindowDelegate>
-{
-@public
-    CVDisplayLinkRef displayLink;
-    NSRecursiveLock* gameLock;
-
-@protected
-    Game* _game;
-    unsigned int _gestureEvents;
-}    
-- (void) detectGamepads: (Game*) game;
-
-@end
-
-
-@implementation View
-
--(void)windowWillClose:(NSNotification*)note 
-{
-    [gameLock lock];
-    _game->exit();
-    [gameLock unlock];
-    [[NSApplication sharedApplication] terminate:self];
-}
-
-- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
-{
-    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-    
-    [self update];
-    
-    [pool release];
-    
-    return kCVReturnSuccess;
-}
-
-- (void) detectGamepads: (Game*) game
-{
-    // Locate any newly connected devices
-    for(HIDGamepad* gamepad in __gamepads)
-    {
-        NSNumber* locationID = [gamepad locationID];
-        if([__activeGamepads objectForKey:locationID] == NULL)
-        {            
-            // Gameplay::add is friended to Platform, but we're not in Platform right now.
-            Platform::gamepadEventConnectedInternal((unsigned int)[locationID intValue],
-                                                    [gamepad numberOfButtons],
-                                                    [gamepad numberOfSticks],
-                                                    [gamepad numberOfTriggerButtons],
-                                                    [gamepad vendorID],
-                                                    [gamepad productID],
-                                                    [[gamepad manufacturerName] cStringUsingEncoding:NSASCIIStringEncoding],
-                                                    [[gamepad productName] cStringUsingEncoding:NSASCIIStringEncoding]);
-
-            [__activeGamepads setObject:locationID forKey:locationID];
-        }
-    }
-    
-    // Detect any disconnected gamepads
-    NSMutableArray* deadGamepads = [NSMutableArray array];
-    for(NSNumber* locationID in __activeGamepads)
-    {
-        HIDGamepad* gamepad = gamepadForLocationID(locationID);
-        if(gamepad == NULL)
-        {
-            NSNumber* gameHandle = [__activeGamepads objectForKey:locationID];
-            Platform::gamepadEventDisconnectedInternal((unsigned int)[locationID intValue]);
-            [deadGamepads addObject:locationID];
-        }
-    }
-    [__activeGamepads removeObjectsForKeys:deadGamepads];
-}
-
--(void) update
-{       
-    [gameLock lock];
-
-    [[self openGLContext] makeCurrentContext];
-    CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
-    if (_game)
-    {
-        [self detectGamepads: _game];
-        
-        _game->frame();
-    }
-    CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
-    CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
-
-    [gameLock 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
-{
-    _game = Game::getInstance();
-    
-    Properties* config = _game->getConfig()->getNamespace("window", true);
-    int samples = config ? config->getInt("samples") : 0;
-    if (samples < 0)
-        samples = 0;
-    
-    // Note: Keep multisampling attributes at the start of the attribute lists since code below
-    // assumes they are array elements 0 through 4.
-    NSOpenGLPixelFormatAttribute windowedAttrs[] = 
-    {
-        NSOpenGLPFAMultisample,
-        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
-        NSOpenGLPFASamples, samples,
-        NSOpenGLPFAAccelerated,
-        NSOpenGLPFADoubleBuffer,
-        NSOpenGLPFAColorSize, 32,
-        NSOpenGLPFADepthSize, 24,
-        NSOpenGLPFAAlphaSize, 8,
-        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
-        0
-    };
-    NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
-    {
-        NSOpenGLPFAMultisample,
-        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
-        NSOpenGLPFASamples, samples,
-        NSOpenGLPFADoubleBuffer,
-        NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
-        NSOpenGLPFAFullScreen,
-        NSOpenGLPFAColorSize, 32,
-        NSOpenGLPFADepthSize, 24,
-        NSOpenGLPFAAlphaSize, 8,
-        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
-        0
-    };
-    NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
-    
-    // Try to choose a supported pixel format
-    NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
-    if (!pf)
-    {
-        bool valid = false;
-        while (!pf && samples > 0)
-        {
-            samples /= 2;
-            attrs[2] = samples ? 1 : 0;
-            attrs[4] = samples;
-            pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
-            if (pf)
-            {
-                valid = true;
-                break;
-            }
-        }
-        
-        if (!valid)
-        {
-            NSLog(@"OpenGL pixel format not supported.");
-            GP_ERROR("Failed to create a valid OpenGL pixel format.");
-            return nil;
-        }
-    }
-    
-    if ((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
-    {
-        gameLock = [[NSRecursiveLock alloc] init];
-        __timeStart = getMachTimeInMilliseconds();
-    }
-    
-    return self;
-}
-
-- (void) prepareOpenGL
-{
-    [super prepareOpenGL];
-    
-    _game->run();
-    
-    if (__fullscreen)
-    {
-        [[self window] setLevel: NSMainMenuWindowLevel+1];
-        [[self window] setHidesOnDeactivate:YES]; 
-    }
-    else
-    {
-        [[self window] setLevel: NSNormalWindowLevel];
-    }
-    [[self window] makeKeyAndOrderFront: self];
-    [[self window] setTitle: [NSString stringWithUTF8String: __title ? __title : ""]];
-    
-    // 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);
-    
-    GLint dim[2] = {__width, __height};
-    CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim);
-    CGLEnable(cglContext, kCGLCESurfaceBackingSize);
-    
-    // Activate the display link
-    CVDisplayLinkStart(displayLink);
-}
-
-- (void) dealloc
-{   
-    [gameLock lock];
-    
-    // Release the display link
-    CVDisplayLinkStop(displayLink);
-    CVDisplayLinkRelease(displayLink);
-    _game->exit();
-    
-    [gameLock unlock];
-
-    [super dealloc];
-}
-
-- (void)resumeDisplayRenderer 
-{
-    [gameLock lock];
-    CVDisplayLinkStop(displayLink);
-    [gameLock unlock]; 
-}
-
-- (void)haltDisplayRenderer 
-{
-    [gameLock lock];
-    CVDisplayLinkStop(displayLink);
-    [gameLock unlock];
-}
-
-- (void) mouse: (Mouse::MouseEvent) mouseEvent orTouchEvent: (Touch::TouchEvent) touchEvent x: (float) x y: (float) y s: (int) s 
-{
-    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, s))
-    {
-        gameplay::Platform::touchEventInternal(touchEvent, x, y, 0);
-    }
-}
-
-- (void) mouseDown: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    __leftMouseDown = true;
-    [self mouse: Mouse::MOUSE_PRESS_LEFT_BUTTON orTouchEvent: Touch::TOUCH_PRESS x: point.x y: __height - point.y s: 0];
-}
-
-- (void) mouseUp: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    __leftMouseDown = false;
-    [self mouse: Mouse::MOUSE_RELEASE_LEFT_BUTTON orTouchEvent: Touch::TOUCH_RELEASE x: point.x y: __height - point.y s: 0];
-
-}
-
-- (void)mouseMoved:(NSEvent*) event 
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    
-    float y;
-    if (__mouseCaptured)
-    {
-        if (__mouseCapturedFirstPass)
-        {
-            // Discard the first mouseMoved event following transition into capture
-            // since it contains bogus x,y data.
-            __mouseCapturedFirstPass = false;
-            return;
-        }
-
-        point.x = [event deltaX];
-        point.y = [event deltaY];
-
-        NSWindow* window = __view.window;
-        NSRect rect = window.frame;
-        CGPoint centerPoint;
-        centerPoint.x = rect.origin.x + (rect.size.width / 2);
-        centerPoint.y = rect.origin.y + (rect.size.height / 2);
-        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
-        y = point.y;
-    }
-    else
-    {
-        y = __height - point.y;
-    }
-    
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, y, 0);
-}
-
-- (void) mouseDragged: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    if (__leftMouseDown && !__mouseCaptured)
-    {
-        [self mouse: Mouse::MOUSE_MOVE orTouchEvent: Touch::TOUCH_MOVE x: point.x y: __height - point.y s: 0];
-    }
-}
-
-- (void) rightMouseDown: (NSEvent*) event
-{
-    __rightMouseDown = true;
-     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    __lx = point.x;
-    __ly = __height - point.y;
-    
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
-}
-
-- (void) rightMouseUp: (NSEvent*) event
-{
-    __rightMouseDown = false;
-    NSPoint point = [event locationInWindow];
-    
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
-}
-
-- (void) rightMouseDragged: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    if (__rightMouseDown)
-    {
-        // Update the pitch and roll by adding the scaled deltas.
-        __roll += (float)(point.x - __lx) * ACCELEROMETER_FACTOR_X;
-        __pitch -= -(float)(point.y - (__height - __ly)) * ACCELEROMETER_FACTOR_Y;
-    
-        // 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 = (__height - point.y);
-    }
-    
-    // In right-mouse case, whether __rightMouseDown is true or false
-    // this should not matter, mouse move is still occuring
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
-}
-
-- (void)otherMouseDown: (NSEvent*) event 
-{
-    __otherMouseDown = true;
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
-}
-
-- (void)otherMouseUp: (NSEvent*) event 
-{
-    __otherMouseDown = false;
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
-}
-
-- (void)otherMouseDragged: (NSEvent*) event 
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
-}
-
-- (void) mouseEntered: (NSEvent*)event
-{
-    __hasMouse = true;
-}
-
-- (void)scrollWheel: (NSEvent*) event 
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
-}
-
-- (void) mouseExited: (NSEvent*)event
-{
-    __leftMouseDown = false;
-    __rightMouseDown = false;
-    __otherMouseDown = false;
-    __hasMouse = false;
-}
-
-- (BOOL)acceptsFirstResponder
-{
-    return YES;
-}
-
-int getKey(unsigned short keyCode, unsigned int modifierFlags)
-{
-    __shiftDown = (modifierFlags & NSShiftKeyMask);
-    unsigned int caps = (__shiftDown ? 1 : 0) ^ ((modifierFlags & NSAlphaShiftKeyMask) ? 1 : 0);
-    switch(keyCode)
-    {
-        case 0x69:
-            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 caps ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
-        case 0x0B:
-            return caps ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
-        case 0x08:
-            return caps ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
-        case 0x02:
-            return caps ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
-        case 0x0E:
-            return caps ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
-        case 0x03:
-            return caps ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
-        case 0x05:
-            return caps ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
-        case 0x04:
-            return caps ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
-        case 0x22:
-            return caps ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
-        case 0x26:
-            return caps ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
-        case 0x28:
-            return caps ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
-        case 0x25:
-            return caps ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
-        case 0x2E:
-            return caps ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
-        case 0x2D:
-            return caps ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
-        case 0x1F:
-            return caps ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
-        case 0x23:
-            return caps ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
-        case 0x0C:
-            return caps ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
-        case 0x0F:
-            return caps ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
-        case 0x01:
-            return caps ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
-        case 0x11:
-            return caps ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
-        case 0x20:
-            return caps ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
-        case 0x09:
-            return caps ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
-        case 0x0D:
-            return caps ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
-        case 0x07:
-            return caps ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
-        case 0x10:
-            return caps ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
-        case 0x06:
-            return caps ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
-
-        default:
-            return Keyboard::KEY_NONE;
-    }
-}
-
-/**
- * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
- */
-int getUnicode(int key)
-{
-    
-    switch (key)
-    {
-        case Keyboard::KEY_BACKSPACE:
-            return 0x0008;
-        case Keyboard::KEY_TAB:
-            return 0x0009;
-        case Keyboard::KEY_RETURN:
-        case Keyboard::KEY_KP_ENTER:
-            return 0x000A;
-        case Keyboard::KEY_ESCAPE:
-            return 0x001B;
-        case Keyboard::KEY_SPACE:
-        case Keyboard::KEY_EXCLAM:
-        case Keyboard::KEY_QUOTE:
-        case Keyboard::KEY_NUMBER:
-        case Keyboard::KEY_DOLLAR:
-        case Keyboard::KEY_PERCENT:
-        case Keyboard::KEY_CIRCUMFLEX:
-        case Keyboard::KEY_AMPERSAND:
-        case Keyboard::KEY_APOSTROPHE:
-        case Keyboard::KEY_LEFT_PARENTHESIS:
-        case Keyboard::KEY_RIGHT_PARENTHESIS:
-        case Keyboard::KEY_ASTERISK:
-        case Keyboard::KEY_PLUS:
-        case Keyboard::KEY_COMMA:
-        case Keyboard::KEY_MINUS:
-        case Keyboard::KEY_PERIOD:
-        case Keyboard::KEY_SLASH:
-        case Keyboard::KEY_ZERO:
-        case Keyboard::KEY_ONE:
-        case Keyboard::KEY_TWO:
-        case Keyboard::KEY_THREE:
-        case Keyboard::KEY_FOUR:
-        case Keyboard::KEY_FIVE:
-        case Keyboard::KEY_SIX:
-        case Keyboard::KEY_SEVEN:
-        case Keyboard::KEY_EIGHT:
-        case Keyboard::KEY_NINE:
-        case Keyboard::KEY_COLON:
-        case Keyboard::KEY_SEMICOLON:
-        case Keyboard::KEY_LESS_THAN:
-        case Keyboard::KEY_EQUAL:
-        case Keyboard::KEY_GREATER_THAN:
-        case Keyboard::KEY_QUESTION:
-        case Keyboard::KEY_AT:
-        case Keyboard::KEY_CAPITAL_A:
-        case Keyboard::KEY_CAPITAL_B:
-        case Keyboard::KEY_CAPITAL_C:
-        case Keyboard::KEY_CAPITAL_D:
-        case Keyboard::KEY_CAPITAL_E:
-        case Keyboard::KEY_CAPITAL_F:
-        case Keyboard::KEY_CAPITAL_G:
-        case Keyboard::KEY_CAPITAL_H:
-        case Keyboard::KEY_CAPITAL_I:
-        case Keyboard::KEY_CAPITAL_J:
-        case Keyboard::KEY_CAPITAL_K:
-        case Keyboard::KEY_CAPITAL_L:
-        case Keyboard::KEY_CAPITAL_M:
-        case Keyboard::KEY_CAPITAL_N:
-        case Keyboard::KEY_CAPITAL_O:
-        case Keyboard::KEY_CAPITAL_P:
-        case Keyboard::KEY_CAPITAL_Q:
-        case Keyboard::KEY_CAPITAL_R:
-        case Keyboard::KEY_CAPITAL_S:
-        case Keyboard::KEY_CAPITAL_T:
-        case Keyboard::KEY_CAPITAL_U:
-        case Keyboard::KEY_CAPITAL_V:
-        case Keyboard::KEY_CAPITAL_W:
-        case Keyboard::KEY_CAPITAL_X:
-        case Keyboard::KEY_CAPITAL_Y:
-        case Keyboard::KEY_CAPITAL_Z:
-        case Keyboard::KEY_LEFT_BRACKET:
-        case Keyboard::KEY_BACK_SLASH:
-        case Keyboard::KEY_RIGHT_BRACKET:
-        case Keyboard::KEY_UNDERSCORE:
-        case Keyboard::KEY_GRAVE:
-        case Keyboard::KEY_A:
-        case Keyboard::KEY_B:
-        case Keyboard::KEY_C:
-        case Keyboard::KEY_D:
-        case Keyboard::KEY_E:
-        case Keyboard::KEY_F:
-        case Keyboard::KEY_G:
-        case Keyboard::KEY_H:
-        case Keyboard::KEY_I:
-        case Keyboard::KEY_J:
-        case Keyboard::KEY_K:
-        case Keyboard::KEY_L:
-        case Keyboard::KEY_M:
-        case Keyboard::KEY_N:
-        case Keyboard::KEY_O:
-        case Keyboard::KEY_P:
-        case Keyboard::KEY_Q:
-        case Keyboard::KEY_R:
-        case Keyboard::KEY_S:
-        case Keyboard::KEY_T:
-        case Keyboard::KEY_U:
-        case Keyboard::KEY_V:
-        case Keyboard::KEY_W:
-        case Keyboard::KEY_X:
-        case Keyboard::KEY_Y:
-        case Keyboard::KEY_Z:
-        case Keyboard::KEY_LEFT_BRACE:
-        case Keyboard::KEY_BAR:
-        case Keyboard::KEY_RIGHT_BRACE:
-        case Keyboard::KEY_TILDE:
-            return key;
-        default:
-            return 0;
-    }
-}
-
-- (void)flagsChanged: (NSEvent*)event
-{
-    unsigned int keyCode = [event keyCode];
-    unsigned int flags = [event modifierFlags];
-    switch (keyCode) 
-    {
-        case 0x39:
-            gameplay::Platform::keyEventInternal((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
-            break;
-        case 0x38:
-            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
-            break;
-        case 0x3C:
-            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
-            break;
-        case 0x3A:
-            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
-            break;
-        case 0x3D:
-            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
-            break;
-        case 0x3B:
-            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
-            break;
-        case 0x3E:
-            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
-            break;
-        case 0x37:
-            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
-            break;
-        case 0x36:
-            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
-            break;
-    }
-}
-
-- (void) keyDown: (NSEvent*) event
-{
-    if ([event isARepeat] == NO)
-    {
-        int key = getKey([event keyCode], [event modifierFlags]);
-        gameplay::Platform::keyEventInternal(Keyboard::KEY_PRESS, key);
-        
-        int character = getUnicode(key);
-        if (character)
-        {
-            gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
-        }
-    }
-}
-
-- (void) keyUp: (NSEvent*) event
-{    
-    gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey([event keyCode], [event modifierFlags]));
-}
-
-// Gesture support for Mac OS X Trackpads
-- (bool)isGestureRegistered: (Gesture::GestureEvent) evt
-{
-    return ((_gestureEvents & evt) == evt);
-}
-
-- (void)registerGesture: (Gesture::GestureEvent) evt
-{
-    _gestureEvents |= evt;
-}
-
-- (void)unregisterGesture: (Gesture::GestureEvent) evt
-{
-    _gestureEvents &= (~evt);
-}
-
-- (void)magnifyWithEvent:(NSEvent *)event
-{
-    if([self isGestureRegistered:Gesture::GESTURE_PINCH] == false) return;
-    
-    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny  inView:nil];
-    // Approximate the center by adding and averageing for now
-    // Note this is centroid on the physical device be used for touching, not the display
-    float xavg = 0.0f;
-    float yavg = 0.0f;
-    for(NSTouch *t in touches) {
-        xavg += [t normalizedPosition].x;
-        yavg += [t normalizedPosition].y;
-    }
-    xavg /= [touches count];
-    yavg /= [touches count];
-    
-    [gameLock lock];
-    _game->gesturePinchEvent((int)xavg, (int)yavg, [event magnification]);
-    [gameLock unlock];
-}
-
-
-@end
-
-@interface FullscreenWindow : NSWindow
-{ 
-}
-@end
-
-@implementation FullscreenWindow
-- (BOOL)canBecomeKeyWindow
-{
-    return YES;
-}
-@end
-
-
-namespace gameplay
-{
-
-extern void print(const char* format, ...)
-{
-    GP_ASSERT(format);
-    va_list argptr;
-    va_start(argptr, format);
-    vfprintf(stderr, format, argptr);
-    va_end(argptr);
-}
-    
-Platform::Platform(Game* game)
-: _game(game)
-{
-    __activeGamepads = [[NSMutableDictionary alloc] init];
-    __gamepads = [[NSMutableArray alloc] init];
-    __hidManagerRef = IOHIDManagerCreate(CFAllocatorGetDefault(), kIOHIDOptionsTypeNone);
-    IOHIDManagerRegisterDeviceMatchingCallback(__hidManagerRef, hidDeviceDiscoveredCallback, NULL);
-    IOHIDManagerRegisterDeviceRemovalCallback(__hidManagerRef, hidDeviceRemovalCallback, NULL);
-    
-    CFDictionaryRef matchingCFDictRef = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
-    if (matchingCFDictRef) IOHIDManagerSetDeviceMatching(__hidManagerRef, matchingCFDictRef);
-    CFRelease(matchingCFDictRef);
-    
-    IOHIDManagerScheduleWithRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOReturn kr = IOHIDManagerOpen(__hidManagerRef, kIOHIDOptionsTypeNone);
-    assert(kr == 0);
-}
-
-    
-Platform::~Platform()
-{
-    IOHIDManagerUnscheduleFromRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDManagerClose(__hidManagerRef, kIOHIDOptionsTypeNone);
-    
-    CFRelease(__hidManagerRef);
-    __hidManagerRef = NULL;
-    [__activeGamepads release];
-    __activeGamepads = NULL;
-    [__gamepads release];
-    __gamepads = NULL;
-}
-
-    
-Platform* Platform::create(Game* game, void* attachToWindow)
-{
-    __attachToWindow = attachToWindow;
-    Platform* platform = new Platform(game);
-    
-    return platform;
-}
-
-int Platform::enterMessagePump()
-{
-    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
-    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
-    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
-    
-    // Read window settings from config.
-    if (_game->getConfig())
-    {
-        Properties* config = _game->getConfig()->getNamespace("window", true);
-        if (config)
-        {
-            // Read window title.
-            __title = const_cast<char *>(config->getString("title"));
-
-            // Read window size.
-            int width = config->getInt("width");
-            if (width != 0)
-                __width = width;
-            int height = config->getInt("height");
-            if (height != 0)
-                __height = height;
-
-            // Read fullscreen state.
-            __fullscreen = config->getBool("fullscreen");
-            if (__fullscreen && width == 0 && height == 0)
-            {
-                CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
-                __width = CGRectGetWidth(mainMonitor);
-                __height = CGRectGetHeight(mainMonitor);
-            }
-        }
-    }
-
-    // Set the scale factors for the mouse movement used to simulate the accelerometer.
-    ACCELEROMETER_FACTOR_X = 90.0f / __width;
-    ACCELEROMETER_FACTOR_Y = 90.0f / __height;
-
-    NSAutoreleasePool* pool = [NSAutoreleasePool new];
-    NSApplication* app = [NSApplication sharedApplication];
-    NSRect screenBounds = [[NSScreen mainScreen] frame];
-    NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
-    
-    __view = [[View alloc] initWithFrame:viewBounds];
-    if (__view == NULL)
-    {
-        GP_ERROR("Failed to create view: exiting.");
-        return EXIT_FAILURE;
-    }
-    
-    NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
-                                 NSMidY(screenBounds) - NSMidY(viewBounds),
-                                 viewBounds.size.width, 
-                                 viewBounds.size.height);
-    
-    NSWindow* window = NULL;
-    if (__fullscreen)
-    {
-        window = [[FullscreenWindow alloc]
-                   initWithContentRect:screenBounds
-                   styleMask:NSBorderlessWindowMask
-                   backing:NSBackingStoreBuffered
-                   defer:NO];
-    }
-    else
-    {
-        window = [[NSWindow alloc]
-                   initWithContentRect:centered
-                   styleMask:NSTitledWindowMask | NSClosableWindowMask
-                   backing:NSBackingStoreBuffered
-                   defer:NO];
-    }
-    
-    [window setAcceptsMouseMovedEvents:YES];
-    [window setContentView:__view];
-    [window setDelegate:__view];
-    [__view release];
-    
-    [app run];
-    
-    [pool release];
-    return EXIT_SUCCESS;
-}
-
-void Platform::signalShutdown() 
-{
-    [__view haltDisplayRenderer];
-
-    // Don't perform terminate right away, enqueue to give game object
-    // a chance to cleanup
-    NSApplication* app = [NSApplication sharedApplication];
-    [app performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
-}
-
-bool Platform::canExit()
-{
-    return true;
-}
-    
-unsigned int Platform::getDisplayWidth()
-{
-    return __width;
-}
-
-unsigned int Platform::getDisplayHeight()
-{
-    return __height;
-}
-
-double Platform::getAbsoluteTime()
-{
-    __timeAbsolute = getMachTimeInMilliseconds();
-    return __timeAbsolute;
-}
-
-void Platform::setAbsoluteTime(double time)
-{
-    __timeAbsolute = time;
-}
-
-bool Platform::isVsync()
-{
-    return __vsync;
-}
-
-void Platform::setVsync(bool enable)
-{
-    __vsync = enable;
-    GLint swapInt = enable ? 1 : 0;
-    [[__view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
-}
-
-void Platform::swapBuffers()
-{
-    if (__view)
-        CGLFlushDrawable((CGLContextObj)[[__view openGLContext] CGLContextObj]);
-}
-
-void Platform::sleep(long ms)
-{
-    usleep(ms * 1000);
-}
-
-void Platform::setMultiTouch(bool enabled)
-{
-}
-    
-bool Platform::isMultiTouch()
-{
-    return true;
-}
-    
-void Platform::getAccelerometerValues(float* pitch, float* roll)
-{
-    GP_ASSERT(pitch);
-    GP_ASSERT(roll);
-
-    *pitch = __pitch;
-    *roll = __roll;
-}
-
-bool Platform::hasMouse()
-{
-    return true;
-}
-
-void Platform::setMouseCaptured(bool captured)
-{
-    if (captured != __mouseCaptured)
-    {
-        if (captured)
-        {
-            [NSCursor hide];
-            __mouseCapturedFirstPass = true;
-        }
-        else
-        {   
-            [NSCursor unhide];
-        }
-        NSWindow* window = __view.window;
-        NSRect rect = window.frame;
-        CGPoint centerPoint;
-        centerPoint.x = rect.origin.x + (rect.size.width / 2);
-        centerPoint.y = rect.origin.y + (rect.size.height / 2);
-        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
-        __mouseCaptured = captured;
-    }
-}
-
-bool Platform::isMouseCaptured()
-{
-    return __mouseCaptured;
-}
-
-void Platform::setCursorVisible(bool visible)
-{
-    if (visible != __cursorVisible)
-    {
-        if (visible)
-        {
-             [NSCursor unhide];
-        }
-        else 
-        {
-             [NSCursor hide];
-        }
-        __cursorVisible = visible;
-    }
-}
-
-bool Platform::isCursorVisible()
-{
-    return __cursorVisible;
-}
-
-void Platform::displayKeyboard(bool display)
-{
-    // Do nothing.
-}
-
-void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    [__view->gameLock lock];
-    if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
-        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-        Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
-    }
-    [__view->gameLock unlock];
-}
-    
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    [__view->gameLock lock];
-    if (!Form::keyEventInternal(evt, key))
-    {
-        Game::getInstance()->keyEvent(evt, key);
-        Game::getInstance()->getScriptController()->keyEvent(evt, key);
-    }
-    [__view->gameLock unlock];
-}
-
-bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
-{
-    [__view->gameLock lock];
-    
-    bool result;
-    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
-    {
-        result = true;
-    }
-    else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
-    {
-        result = true;
-    }
-    else
-    {
-        result = Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
-    }
-    
-    [__view->gameLock unlock];
-    
-    return result;
-}
-    
-void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-                                             unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
-{
-    Gamepad::add(handle, buttonCount, joystickCount, triggerCount, vendorId, productId, vendorString, productString);
-}
-
-void Platform::gamepadEventDisconnectedInternal(GamepadHandle handle)
-{
-    Gamepad::remove(handle);
-}
-
-void Platform::shutdownInternal()
-{
-    Game::getInstance()->shutdown();
-}
-
-bool Platform::isGestureSupported(Gesture::GestureEvent evt)
-{
-    // Swipe unsupported as it is considered moving mouse cursor
-    // Two fingers is scrolling
-    // Three fingers is swipe, but is not always enabled on users system
-    // Tap not supported as it is considered a mouse click/button click
-    // on some systems making it difficult to differentiate 
-    switch(evt)
-    {
-        case Gesture::GESTURE_PINCH:
-            return true;
-        default:
-            break;
-    }
-    return false;
-}
-
-void Platform::registerGesture(Gesture::GestureEvent evt)
-{
-    [__view registerGesture:evt];   
-}
-
-void Platform::unregisterGesture(Gesture::GestureEvent evt)
-{
-    [__view unregisterGesture:evt];        
-}
-  
-bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
-{
-     return [__view isGestureRegistered:evt];
-}
-
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-    HIDGamepad* gp = gamepadForGameHandle(gamepad->_handle);
-    
-    if (gp)
-    {
-        // Haven't figured out how to have the triggers not also show up in the buttons array.
-        // So for now a value of -1 means "Don't map this button."
-        static const int PS3Mapping[17] = {
-            Gamepad::BUTTON_MENU1,  // 0x0001
-            Gamepad::BUTTON_L3,     // 0x0002
-            Gamepad::BUTTON_R3,     // 0x0004
-            Gamepad::BUTTON_MENU2,  // 0x0008
-            Gamepad::BUTTON_UP,     // 0x0010
-            Gamepad::BUTTON_RIGHT,  // 0x0020
-            Gamepad::BUTTON_DOWN,   // 0x0040
-            Gamepad::BUTTON_LEFT,   // 0x0080
-            -1,                     // Gamepad::BUTTON_L2,     // 0x0100
-            -1,                     // Gamepad::BUTTON_R2,     // 0x0200
-            Gamepad::BUTTON_L1,     // 0x0400
-            Gamepad::BUTTON_R1,     // 0x0800
-            Gamepad::BUTTON_Y,      // 0x1000
-            Gamepad::BUTTON_B,      // 0x2000
-            Gamepad::BUTTON_A,      // 0x4000
-            Gamepad::BUTTON_X,      // 0x8000
-            Gamepad::BUTTON_MENU3   // 0x10000
-        };
-        
-        const int* mapping = NULL;
-        if (gamepad->_vendorId == SONY_USB_VENDOR_ID &&
-            gamepad->_productId == SONY_USB_PS3_PRODUCT_ID)
-        {
-            mapping = PS3Mapping;
-        }
-        
-        gamepad->_buttons = 0;
-        
-        for (int i = 0; i < [gp numberOfButtons]; ++i)
-        {
-            HIDGamepadButton* b = [gp buttonAtIndex: i];
-            if ([b state])
-            {
-                // This button is down.
-                if (mapping)
-                {
-                    if (mapping[i] >= 0)
-                        gamepad->_buttons |= (1 << mapping[i]);
-                }
-                else
-                {
-                    gamepad->_buttons |= (1 << i);
-                }
-            }
-        }
-
-        for (unsigned int i = 0; i < [gp numberOfSticks]; ++i)
-        {
-            float rawX = [[gp axisAtIndex: i*2] calibratedValue];
-            float rawY = -[[gp axisAtIndex: i*2 + 1] calibratedValue];
-            if (std::fabs(rawX) <= 0.07f)
-                rawX = 0;
-            if (std::fabs(rawY) <= 0.07f)
-                rawY = 0;
-            gamepad->_joysticks[i].x = rawX;
-            gamepad->_joysticks[i].y = rawY;
-        }
-        
-        for (unsigned int i = 0; i < [gp numberOfTriggerButtons]; ++i)
-        {
-            gamepad->_triggers[i] = [[gp triggerButtonAtIndex: i] calibratedStateValue];
-        }
-    }
-}
-
-}
-
-HIDGamepad* gamepadForLocationID(NSNumber* locationID)
-{
-    HIDGamepad* fgamepad = NULL;
-    for(HIDGamepad* gamepad in __gamepads)
-    {
-        if([[gamepad locationID] isEqual:locationID])
-        {
-            fgamepad = gamepad;
-            break;
-        }
-    }
-    return fgamepad;
-}
-
-HIDGamepad* gamepadForLocationIDValue(unsigned int locationIDValue)
-{
-    return gamepadForLocationID([NSNumber numberWithUnsignedInt:locationIDValue]);
-}
-
-HIDGamepad* gamepadForGameHandle(int gameHandle)
-{
-    HIDGamepad* gamepad = NULL;
-    for(NSNumber* locationID in __activeGamepads)
-    {
-        NSNumber* handleID = [__activeGamepads objectForKey:locationID];
-        if([handleID integerValue] == gameHandle)
-        {
-            gamepad = gamepadForLocationID(locationID);
-            break;
-        }
-    }
-    return gamepad;
-}
-
-CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage) 
-{
-    // create a dictionary to add usage page/usages to
-    CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    if (result) {
-        if (inUsagePage) 
-        {
-            // Add key for device type to refine the matching dictionary.
-            CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
-            if (pageCFNumberRef) 
-            {
-                CFDictionarySetValue(result, CFSTR( kIOHIDDeviceUsagePageKey ), pageCFNumberRef);
-                CFRelease(pageCFNumberRef);
-                
-                // note: the usage is only valid if the usage page is also defined
-                if (inUsage) 
-                {
-                    CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
-                    if (usageCFNumberRef) 
-                    {
-                        CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
-                        CFRelease(usageCFNumberRef);
-                    } 
-                    else 
-                    {
-                        fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__);
-                    }
-                }
-            } 
-            else 
-            {
-                fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__);
-            }
-        }
-    } 
-    else 
-    {
-        fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__);
-    }
-    return result;
-}
-
-CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
-{
-    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
-    {
-        return NULL;
-    }
-    return (CFStringRef)typeRef;
-}
-
-int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
-{
-    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
-    {
-        return 0;
-    }
-    
-    int value;
-    CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
-    return value;
-}
-
-static void hidDeviceDiscoveredCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef) 
-{
-    CFNumberRef locID = (CFNumberRef)IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey));
-    if(locID)
-    {
-        HIDGamepad* gamepad = [[HIDGamepad alloc] initWithDevice:inIOHIDDeviceRef];
-        [__gamepads addObject:gamepad];
-    }
-    
-}
-
-static void hidDeviceRemovalCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef) 
-{
-    int removeIndex = -1;
-    NSNumber *locID = (NSNumber*)IOHIDDeviceGetProperty(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey));
-    if(locID)
-    {
-        for(int i = 0; i < [__gamepads count]; i++)
-        {
-            HIDGamepad* gamepad = [__gamepads objectAtIndex:i];
-            if([[gamepad locationID] isEqual:locID])
-            {
-                removeIndex = i;
-                break;
-            }
-        }
-    }
-    if(removeIndex >= 0)
-    {
-        [__gamepads removeObjectAtIndex:removeIndex];
-    }
-}
-
-static void hidDeviceValueAvailableCallback(void* inContext, IOReturn inResult,  void* inSender)
-{
-    HIDGamepad* d = (HIDGamepad*)inContext;
-    do
-    {
-        IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( ( IOHIDQueueRef ) inSender, 0. );
-        if (!valueRef) break;
-        [d hidValueAvailable:valueRef];
-        CFRelease(valueRef); // Don't forget to release our HID value reference
-    } while (1);
-}
-
-bool Platform::launchURL(const char *url)
-{
-    if (url == NULL || *url == '\0')
-        return false;
-
-    CFURLRef urlRef = CFURLCreateWithBytes(
-        NULL,
-        (UInt8*)url,
-        strlen(url),
-        kCFStringEncodingASCII,
-        NULL
-    );
-    const OSStatus err = LSOpenCFURLRef(urlRef, 0);
-    CFRelease(urlRef);
-    return (err == noErr);
-}
-
-#endif
+#ifdef __APPLE__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+#include "Form.h"
+#include "ScriptController.h"
+#include <unistd.h>
+#include <IOKit/hid/IOHIDLib.h>
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CVDisplayLink.h>
+#import <OpenGL/OpenGL.h>
+#import <mach/mach_time.h>
+#import <Foundation/Foundation.h>
+
+// These should probably be moved to a platform common file
+#define SONY_USB_VENDOR_ID              0x054c
+#define SONY_USB_PS3_PRODUCT_ID         0x0268
+#define MICROSOFT_VENDOR_ID             0x045e
+#define MICROSOFT_XBOX360_PRODUCT_ID    0x028e
+#define STEELSERIES_VENDOR_ID           0x1038
+#define STEELSERIES_FREE_PRODUCT_ID     0x1412
+#define FRUCTEL_VENDOR_ID               0x25B6
+#define FRUCTEL_GAMETEL_PRODUCT_ID      0x0001
+
+using namespace std;
+using namespace gameplay;
+
+@class View;
+@class HIDGamepad;
+
+int __argc = 0;
+char** __argv = 0;
+
+// Default to 720p
+static int __width = 1280;
+static int __height = 720;
+
+static float ACCELEROMETER_FACTOR_X = 90.0f / __width;
+static float ACCELEROMETER_FACTOR_Y = 90.0f / __height;
+
+static double __timeStart;
+static double __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 __otherMouseDown = false;
+static bool __shiftDown = false;
+static char* __title = NULL;
+static bool __fullscreen = false;
+static bool __resizable = false;
+static void* __attachToWindow = NULL;
+static bool __mouseCaptured = false;
+static bool __mouseCapturedFirstPass = false;
+static CGPoint __mouseCapturePoint;
+static bool __multiSampling = false;
+static bool __cursorVisible = true;
+static View* __view = NULL;
+
+static NSMutableDictionary *__activeGamepads = NULL;
+static NSMutableArray *__gamepads = NULL;
+static IOHIDManagerRef __hidManagerRef = NULL;
+
+// Gamepad Helper Function
+HIDGamepad *gamepadForLocationID(NSNumber *locationID);
+HIDGamepad *gamepadForLocationIDValue(unsigned int locationIDValue);
+HIDGamepad *gamepadForGameHandle(int gameHandle);
+
+
+// IOHid Helper Functions
+CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage);
+CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
+int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
+
+// IOHid Callbacks
+static void hidDeviceDiscoveredCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+static void hidDeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+static void hidDeviceValueAvailableCallback(void *inContext, IOReturn inResult,  void *inSender);
+
+double getMachTimeInMilliseconds()
+{
+    static const double 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
+    GP_ASSERT(s_timebase_info.denom);
+    return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
+}
+
+@interface HIDGamepadAxis : NSObject
+{
+    IOHIDElementRef e;
+    CFIndex v;
+    CFIndex logMin;
+    CFIndex logMax;
+}
+
++ gamepadAxisWithAxisElement:(IOHIDElementRef)element;
+- initWithAxisElement:(IOHIDElementRef)element;
+- (IOHIDElementRef)element;
+- (IOHIDElementCookie)cookie;
+- (uint32_t)usage;
+- (uint32_t)usagePage;
+- (CFIndex)logicalMinimum;
+- (CFIndex)logicalMaximum;
+
+- (float)calibratedValue;
+- (CFIndex)value;
+- (void)setValue:(CFIndex)value;
+@end
+
+@implementation HIDGamepadAxis
++ gamepadAxisWithAxisElement:(IOHIDElementRef)element
+{
+    return [[[[self class] alloc] initWithAxisElement:element] autorelease];
+}
+
+- initWithAxisElement:(IOHIDElementRef)element
+{
+    if((self = [super init]))
+    {
+        e = (IOHIDElementRef)CFRetain(element);
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    CFRelease(e);
+    [super dealloc];
+}
+
+- (IOHIDElementRef)element
+{
+    return e;
+}
+
+- (IOHIDElementCookie)cookie
+{
+    return IOHIDElementGetCookie(e);
+}
+
+- (bool)isHatSwitch {
+    return (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch);
+}
+
+- (uint32_t)usage
+{
+    return IOHIDElementGetUsage(e);
+}
+
+- (uint32_t)usagePage
+{
+    return IOHIDElementGetUsagePage(e);
+}
+
+- (CFIndex)logicalMinimum
+{
+    return IOHIDElementGetLogicalMin(e);    
+}
+
+- (CFIndex)logicalMaximum
+{
+    return IOHIDElementGetLogicalMax(e);
+}
+
+- (float)calibratedValue
+{
+    float cmax = 2.0f;
+    float cmin = 0.0f;
+    return ((((v - [self logicalMinimum]) * (cmax - cmin)) / ([self logicalMaximum] - [self logicalMinimum])) + cmin - 1.0f);    
+}
+
+- (CFIndex)value
+{
+    return v;
+}
+
+- (void)setValue:(CFIndex)value
+{
+    v = value;
+}
+@end
+
+@interface HIDGamepadButton : NSObject
+{
+    IOHIDElementRef e;
+    IOHIDElementRef te;
+    bool state;
+    int triggerValue;
+}
+
++ gamepadButtonWithButtonElement:(IOHIDElementRef)element;
+- initWithButtonElement:(IOHIDElementRef)element;
+- (void)setTriggerElement:(IOHIDElementRef)element;
+- (IOHIDElementRef)element;
+- (IOHIDElementCookie)cookie;
+- (IOHIDElementRef)triggerElement;
+- (IOHIDElementCookie)triggerCookie;
+
+- (bool)isTriggerButton;
+- (uint32_t)usage;
+- (uint32_t)usagePage;
+- (int)stateValue;
+- (float)calibratedStateValue;
+- (void)setStateValue:(int)value;
+- (bool)state;
+- (void)setState:(bool)state;
+@end
+
+@implementation HIDGamepadButton
++ gamepadButtonWithButtonElement:(IOHIDElementRef)element
+{
+    return [[[[self class] alloc] initWithButtonElement:element] autorelease];
+}
+
+- initWithButtonElement:(IOHIDElementRef)element
+{
+    if((self = [super init]))
+    {
+        e = (IOHIDElementRef)CFRetain(element);
+        te = NULL;
+        state = false;
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    CFRelease(e);
+    if(te != NULL) CFRelease(te);
+    [super dealloc];
+}
+
+- (void)setTriggerElement:(IOHIDElementRef)element {
+    if(te)
+    {
+        CFRelease(te);
+        te = NULL;
+    }
+    if(element)
+    {
+        te = (IOHIDElementRef)CFRetain(element);
+    }
+}
+
+- (IOHIDElementRef)element
+{
+    return e;
+}
+
+- (IOHIDElementCookie)cookie
+{
+    return IOHIDElementGetCookie(e);
+}
+
+- (IOHIDElementRef)triggerElement
+{
+    return te;
+}
+
+- (IOHIDElementCookie)triggerCookie
+{
+    return IOHIDElementGetCookie(te);
+}
+
+- (bool)isTriggerButton
+{
+    return (te != NULL);
+}
+
+- (uint32_t)usage
+{
+    return IOHIDElementGetUsage(e);
+}
+
+- (uint32_t)usagePage
+{
+    return IOHIDElementGetUsagePage(e);
+}
+
+- (void)setStateValue:(int)value {
+    triggerValue = value;
+}
+
+- (int)stateValue
+{
+    return triggerValue;
+}
+
+- (float)calibratedStateValue
+{
+    return (float)triggerValue / 255.0f;
+}
+
+- (bool)state
+{
+    return state;
+}
+
+- (void)setState:(bool)s
+{
+    state = s;
+}
+@end
+
+@interface HIDGamepad : NSObject
+{
+    IOHIDDeviceRef hidDeviceRef;
+    IOHIDQueueRef queueRef;
+    NSMutableArray* buttons;
+    NSMutableArray* triggerButtons;
+    NSMutableArray* axes;
+    HIDGamepadAxis* hatSwitch;
+}
+@property (assign) IOHIDDeviceRef hidDeviceRef;
+@property (assign) IOHIDQueueRef queueRef;
+@property (retain) NSMutableArray* buttons;
+@property (retain) NSMutableArray* triggerButtons;
+@property (retain) NSMutableArray* axes;
+@property (retain) HIDGamepadAxis* hatSwitch;
+
+- initWithDevice:(IOHIDDeviceRef)rawDevice;
+- (IOHIDDeviceRef)rawDevice;
+- (NSNumber*)locationID;
+
+- (void)initializeGamepadElements;
+- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie;
+
+- (bool)startListening;
+- (void)stopListening;
+
+- (NSString*)identifierName;
+- (NSString*)productName;
+- (NSString*)manufacturerName;
+- (NSString*)serialNumber;
+- (int)versionNumber;
+- (int)vendorID;
+- (int)productID;
+
+- (NSUInteger)numberOfAxes;
+- (NSUInteger)numberOfSticks;
+- (NSUInteger)numberOfButtons;
+- (NSUInteger)numberOfTriggerButtons;
+- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index;
+- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index;
+- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index;
+- (HIDGamepadAxis*)getHatSwitch;
+@end
+
+@implementation HIDGamepad
+
+@synthesize hidDeviceRef;
+@synthesize queueRef;
+@synthesize buttons;
+@synthesize triggerButtons;
+@synthesize axes;
+@synthesize hatSwitch;
+
+- initWithDevice:(IOHIDDeviceRef)rawDevice
+{
+    if((self = [super init]))
+    {
+        [self setButtons:[NSMutableArray array]];
+        [self setTriggerButtons:[NSMutableArray array]];
+        [self setAxes:[NSMutableArray array]];
+        hatSwitch = NULL;
+
+        CFRetain(rawDevice);
+        IOHIDQueueRef queue = IOHIDQueueCreate(CFAllocatorGetDefault(), rawDevice, 10, kIOHIDOptionsTypeNone);
+        [self setHidDeviceRef:rawDevice];
+        [self setQueueRef:queue];
+        
+        [self initializeGamepadElements];
+        [self startListening];
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    [self stopListening];
+    
+    CFRelease([self rawDevice]);
+    CFRelease([self queueRef]);
+    [self setQueueRef:NULL];
+    [self setHidDeviceRef:NULL];
+    
+    [self setButtons:NULL];
+    [self setTriggerButtons:NULL];
+    [self setAxes:NULL];
+    if (hatSwitch != NULL)
+    {
+        [hatSwitch dealloc];
+    }
+    
+    [super dealloc];
+}
+
+- (IOHIDDeviceRef)rawDevice
+{
+    return [self hidDeviceRef];
+}
+
+- (NSNumber*)locationID
+{
+    return (NSNumber*)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDLocationIDKey));
+}
+
+- (void)initializeGamepadElements
+{
+    uint32_t vendorID = [self vendorID];
+    uint32_t productID = [self productID];
+    
+    CFArrayRef elements = IOHIDDeviceCopyMatchingElements([self rawDevice], NULL, kIOHIDOptionsTypeNone);
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDElementType type = IOHIDElementGetType(hidElement);
+
+        if (type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis)
+        {
+            uint32_t pageUsage = IOHIDElementGetUsage(hidElement);
+            IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
+
+            switch(pageUsage)
+            {
+                case kHIDUsage_GD_X:
+                case kHIDUsage_GD_Y:
+                case kHIDUsage_GD_Rx:
+                case kHIDUsage_GD_Ry:
+                case kHIDUsage_GD_Z:
+                case kHIDUsage_GD_Rz:
+                {
+                    if (vendorID == MICROSOFT_VENDOR_ID &&
+                        productID == MICROSOFT_XBOX360_PRODUCT_ID &&
+                        (pageUsage == kHIDUsage_GD_Z || pageUsage == kHIDUsage_GD_Rz))
+                    {
+                        HIDGamepadButton* triggerButton = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
+                        [triggerButton setTriggerElement:hidElement];
+                        [[self triggerButtons] addObject:triggerButton];
+                    }
+                    else
+                    {
+                        HIDGamepadAxis* axis = [HIDGamepadAxis gamepadAxisWithAxisElement:hidElement];
+                        [[self axes] addObject:axis];
+                    }
+                    break;
+                }
+                case kHIDUsage_GD_Hatswitch:
+                {
+                    HIDGamepadAxis* hat = [[HIDGamepadAxis alloc] initWithAxisElement:hidElement];
+                    [hat setValue: -1];
+                    hatSwitch = hat;
+                }
+                default:
+                    // Ignore the pointers
+                    // Note: Some of the pointers are for the 6-axis accelerometer in a PS3 controller
+                    // Note: L2/R2 triggers are at cookie 39 and 40 base 10 tied to 9 and 10 button elements
+                    break;
+            }
+
+        }
+        if(type == kIOHIDElementTypeInput_Button)
+        {
+            HIDGamepadButton *button = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
+            [[self buttons] addObject:button];
+        }
+    }
+    // Go back and get proprietary information (e.g. triggers) and associate with appropriate values
+    // Example for other trigger buttons
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDElementType type = IOHIDElementGetType(hidElement);
+        IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
+        
+        // Gamepad specific code
+        if(vendorID == SONY_USB_VENDOR_ID && productID == SONY_USB_PS3_PRODUCT_ID)
+        {
+            if((unsigned long)cookie == 39)
+            {
+                HIDGamepadButton *leftTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)9];
+                if(leftTriggerButton)
+                {
+                    [leftTriggerButton setTriggerElement:hidElement];
+                    [[self triggerButtons] addObject:leftTriggerButton];
+                }
+            }
+            if((unsigned long)cookie == 40)
+            {
+                HIDGamepadButton *rightTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)10];
+                if(rightTriggerButton)
+                {
+                    [rightTriggerButton setTriggerElement:hidElement];
+                    [[self triggerButtons] addObject:rightTriggerButton];
+                }
+            }
+        }
+    }
+}
+
+- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie {
+    for(HIDGamepadButton *b in [self buttons]) {
+        if([b cookie] == cookie)
+            return b;
+    }
+    return NULL;
+}
+
+- (bool)startListening
+{
+    IOReturn kr = IOHIDDeviceOpen([self hidDeviceRef], kIOHIDOptionsTypeNone);
+    if(kr != 0) {
+        return false;
+    }
+    IOHIDDeviceScheduleWithRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    
+    IOHIDQueueStart([self queueRef]);
+    IOHIDQueueRegisterValueAvailableCallback([self queueRef], hidDeviceValueAvailableCallback, self);
+    
+    CFArrayRef elements = (CFArrayRef)[self watchedElements];
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDQueueAddElement([self queueRef], hidElement);
+    }
+    
+    IOHIDQueueScheduleWithRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    
+    return true;
+}
+
+- (void)stopListening
+{
+    IOHIDQueueUnscheduleFromRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDQueueStop([self queueRef]);
+    
+    IOHIDDeviceUnscheduleFromRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDDeviceClose([self hidDeviceRef], kIOHIDOptionsTypeNone);
+}
+
+- (NSString*)identifierName
+{
+    NSString* idName = NULL;
+    if(idName == NULL) idName = [self productName];
+    if(idName == NULL) idName = [self manufacturerName];
+    if(idName == NULL) idName = [self serialNumber];
+    if(idName == NULL) idName = [NSString stringWithFormat:@"%d-%d", [self vendorID], [self productID]];
+    return idName;
+}
+
+- (NSString*)productName
+{
+    CFStringRef productName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDProductKey));
+    if(productName == NULL || CFGetTypeID(productName) != CFStringGetTypeID())
+    {
+        return NULL;
+    }
+    return (NSString*)productName;
+}
+
+- (NSString*)manufacturerName
+{
+    CFStringRef manufacturerName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDManufacturerKey));
+    if(manufacturerName == NULL || CFGetTypeID(manufacturerName) != CFStringGetTypeID())
+    {
+        return NULL;
+    }
+    return (NSString*)manufacturerName;
+}
+
+- (NSString*)serialNumber
+{
+    CFStringRef serialNumber = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDSerialNumberKey));
+    if(serialNumber == NULL || CFGetTypeID(serialNumber) != CFStringGetTypeID())
+    {
+        return NULL;
+    }
+    return (NSString*)serialNumber;
+}
+
+- (int)versionNumber
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVersionNumberKey));
+}
+
+- (int)vendorID
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVendorIDKey));
+}
+
+- (int)productID
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDProductIDKey));
+}
+
+- (NSUInteger)numberOfAxes
+{
+    return [[self axes] count];
+}
+
+- (NSUInteger)numberOfSticks
+{
+    return ([[self axes] count] / 2);
+}
+
+- (NSUInteger)numberOfButtons
+{
+    return [[self buttons] count];
+}
+
+- (NSUInteger)numberOfTriggerButtons
+{
+    return [[self triggerButtons] count];
+}
+
+- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index
+{
+    HIDGamepadButton *b = NULL;
+    if(index < [[self triggerButtons] count])
+    {
+        b = [[self triggerButtons] objectAtIndex:index];
+    }
+    return b;
+}
+
+- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index
+{
+    HIDGamepadAxis *a = NULL;
+    if(index < [[self axes] count])
+    {
+        a = [[self axes] objectAtIndex:index];
+    }
+    return a;
+}
+
+- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index
+{
+    HIDGamepadButton *b = NULL;
+    if(index < [[self buttons] count])
+    {
+        b = [[self buttons] objectAtIndex:index];
+    }
+    return b;
+}
+
+- (HIDGamepadAxis*)getHatSwitch
+{
+    if (hatSwitch != NULL)
+    {
+        return hatSwitch;
+    }
+    return NULL;
+}
+
+- (NSArray*)watchedElements
+{
+    NSMutableArray *r = [NSMutableArray array];
+    for(HIDGamepadButton *b in [self buttons])
+    {
+        [r addObject:(id)[b element]];
+    }
+    for(HIDGamepadAxis *a in [self axes])
+    {
+        [r addObject:(id)[a element]];
+    }
+    for(HIDGamepadButton* t in [self triggerButtons])
+    {
+        [r addObject:(id)[t triggerElement]];
+    }
+    if (hatSwitch)
+    {
+        [r addObject:(id)[hatSwitch element]];
+    }
+    return [NSArray arrayWithArray:r];
+}
+
+- (void)hidValueAvailable:(IOHIDValueRef)value
+{
+    IOHIDElementRef element = IOHIDValueGetElement(value);
+    IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
+    
+    if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
+    CFIndex integerValue = IOHIDValueGetIntegerValue(value);
+    
+    for(HIDGamepadAxis *a in [self axes])
+    {
+        if([a cookie] == cookie)
+        {
+            [a setValue:integerValue];
+        }
+    }
+    
+    for(HIDGamepadButton *b in [self buttons])
+    {
+        if([b cookie] == cookie)
+        {
+            [b setState:(bool)integerValue];
+            break;
+        }
+    }
+    
+    for(HIDGamepadButton *b in [self triggerButtons])
+    {
+        if([b triggerCookie] == cookie)
+        {
+            [b setStateValue:integerValue];
+            break;
+        }
+    }
+
+    if (hatSwitch && [hatSwitch cookie] == cookie)
+    {
+        [hatSwitch setValue:integerValue];
+    }
+}
+@end
+
+
+@interface View : NSOpenGLView <NSWindowDelegate>
+{
+@public
+    CVDisplayLinkRef displayLink;
+    NSRecursiveLock* gameLock;
+
+@protected
+    Game* _game;
+    unsigned int _gestureEvents;
+}    
+- (void) detectGamepads: (Game*) game;
+
+@end
+
+
+@implementation View
+
+-(void)windowWillClose:(NSNotification*)note 
+{
+    [gameLock lock];
+    _game->exit();
+    [gameLock unlock];
+    [[NSApplication sharedApplication] terminate:self];
+}
+
+- (void)windowDidResize:(NSNotification*)notification
+{
+    [gameLock lock];
+    NSSize size = [ [ _window contentView ] frame ].size;
+    gameplay::Platform::resizeEventInternal((unsigned int)size.width, (unsigned int)size.height);
+    [gameLock unlock];
+}
+
+- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
+{
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    
+    [self update];
+    
+    [pool release];
+    
+    return kCVReturnSuccess;
+}
+
+- (void) detectGamepads: (Game*) game
+{
+    // Locate any newly connected devices
+    for(HIDGamepad* gamepad in __gamepads)
+    {
+        NSNumber* locationID = [gamepad locationID];
+        if([__activeGamepads objectForKey:locationID] == NULL)
+        {            
+            // Gameplay::add is friended to Platform, but we're not in Platform right now.
+            Platform::gamepadEventConnectedInternal((unsigned int)[locationID intValue],
+                                                    [gamepad numberOfButtons],
+                                                    [gamepad numberOfSticks],
+                                                    [gamepad numberOfTriggerButtons],
+                                                    [gamepad vendorID],
+                                                    [gamepad productID],
+                                                    [[gamepad manufacturerName] cStringUsingEncoding:NSASCIIStringEncoding],
+                                                    [[gamepad productName] cStringUsingEncoding:NSASCIIStringEncoding]);
+
+            [__activeGamepads setObject:locationID forKey:locationID];
+        }
+    }
+    
+    // Detect any disconnected gamepads
+    NSMutableArray* deadGamepads = [NSMutableArray array];
+    for(NSNumber* locationID in __activeGamepads)
+    {
+        HIDGamepad* gamepad = gamepadForLocationID(locationID);
+        if(gamepad == NULL)
+        {
+            NSNumber* gameHandle = [__activeGamepads objectForKey:locationID];
+            Platform::gamepadEventDisconnectedInternal((unsigned int)[locationID intValue]);
+            [deadGamepads addObject:locationID];
+        }
+    }
+    [__activeGamepads removeObjectsForKeys:deadGamepads];
+}
+
+-(void) update
+{       
+    [gameLock lock];
+
+    [[self openGLContext] makeCurrentContext];
+    CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
+    if (_game)
+    {
+        [self detectGamepads: _game];
+        
+        _game->frame();
+    }
+    CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
+    CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
+
+    [gameLock 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
+{
+    _game = Game::getInstance();
+    
+    Properties* config = _game->getConfig()->getNamespace("window", true);
+    int samples = config ? config->getInt("samples") : 0;
+    if (samples < 0)
+        samples = 0;
+    
+    // Note: Keep multisampling attributes at the start of the attribute lists since code below
+    // assumes they are array elements 0 through 4.
+    NSOpenGLPixelFormatAttribute windowedAttrs[] = 
+    {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
+        NSOpenGLPFAAccelerated,
+        NSOpenGLPFADoubleBuffer,
+        NSOpenGLPFAColorSize, 32,
+        NSOpenGLPFADepthSize, 24,
+        NSOpenGLPFAAlphaSize, 8,
+        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
+        0
+    };
+    NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
+    {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
+        NSOpenGLPFADoubleBuffer,
+        NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
+        NSOpenGLPFAFullScreen,
+        NSOpenGLPFAColorSize, 32,
+        NSOpenGLPFADepthSize, 24,
+        NSOpenGLPFAAlphaSize, 8,
+        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
+        0
+    };
+    NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
+    
+    __multiSampling = samples > 0;
+
+    // Try to choose a supported pixel format
+    NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+    if (!pf)
+    {
+        bool valid = false;
+        while (!pf && samples > 0)
+        {
+            samples /= 2;
+            attrs[2] = samples ? 1 : 0;
+            attrs[4] = samples;
+            pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+            if (pf)
+            {
+                valid = true;
+                break;
+            }
+        }
+
+        __multiSampling = samples > 0;
+        
+        if (!valid)
+        {
+            NSLog(@"OpenGL pixel format not supported.");
+            GP_ERROR("Failed to create a valid OpenGL pixel format.");
+            return nil;
+        }
+    }
+    
+    if ((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
+    {
+        gameLock = [[NSRecursiveLock alloc] init];
+        __timeStart = getMachTimeInMilliseconds();
+    }
+    
+    return self;
+}
+
+- (void) prepareOpenGL
+{
+    [super prepareOpenGL];
+    
+    _game->run();
+    
+    if (__fullscreen)
+    {
+        [[self window] setLevel: NSMainMenuWindowLevel+1];
+        [[self window] setHidesOnDeactivate:YES]; 
+    }
+    else
+    {
+        [[self window] setLevel: NSNormalWindowLevel];
+    }
+    [[self window] makeKeyAndOrderFront: self];
+    [[self window] setTitle: [NSString stringWithUTF8String: __title ? __title : ""]];
+    
+    // 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);
+    
+    GLint dim[2] = {__width, __height};
+    CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim);
+    CGLEnable(cglContext, kCGLCESurfaceBackingSize);
+    
+    // Activate the display link
+    CVDisplayLinkStart(displayLink);
+}
+
+- (void) dealloc
+{   
+    [gameLock lock];
+    
+    // Release the display link
+    CVDisplayLinkStop(displayLink);
+    CVDisplayLinkRelease(displayLink);
+    _game->exit();
+    
+    [gameLock unlock];
+
+    [super dealloc];
+}
+
+- (void)resumeDisplayRenderer 
+{
+    [gameLock lock];
+    CVDisplayLinkStop(displayLink);
+    [gameLock unlock]; 
+}
+
+- (void)haltDisplayRenderer 
+{
+    [gameLock lock];
+    CVDisplayLinkStop(displayLink);
+    [gameLock unlock];
+}
+
+- (void) mouse: (Mouse::MouseEvent) mouseEvent orTouchEvent: (Touch::TouchEvent) touchEvent x: (float) x y: (float) y s: (int) s 
+{
+    [__view->gameLock lock];
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, s))
+    {
+        gameplay::Platform::touchEventInternal(touchEvent, x, y, 0);
+    }
+    [__view->gameLock unlock];
+}
+
+- (void) mouseDown: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    __leftMouseDown = true;
+    [self mouse: Mouse::MOUSE_PRESS_LEFT_BUTTON orTouchEvent: Touch::TOUCH_PRESS x: point.x y: __height - point.y s: 0];
+}
+
+- (void) mouseUp: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    __leftMouseDown = false;
+    [self mouse: Mouse::MOUSE_RELEASE_LEFT_BUTTON orTouchEvent: Touch::TOUCH_RELEASE x: point.x y: __height - point.y s: 0];
+
+}
+
+- (void)mouseMoved:(NSEvent*) event 
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    
+    float y;
+    if (__mouseCaptured)
+    {
+        if (__mouseCapturedFirstPass)
+        {
+            // Discard the first mouseMoved event following transition into capture
+            // since it contains bogus x,y data.
+            __mouseCapturedFirstPass = false;
+            return;
+        }
+
+        point.x = [event deltaX];
+        point.y = [event deltaY];
+
+        NSWindow* window = __view.window;
+        NSRect rect = window.frame;
+        CGPoint centerPoint;
+        centerPoint.x = rect.origin.x + (rect.size.width / 2);
+        centerPoint.y = rect.origin.y + (rect.size.height / 2);
+        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
+        y = point.y;
+    }
+    else
+    {
+        y = __height - point.y;
+    }
+    
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) mouseDragged: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    if (__leftMouseDown && !__mouseCaptured)
+    {
+        [self mouse: Mouse::MOUSE_MOVE orTouchEvent: Touch::TOUCH_MOVE x: point.x y: __height - point.y s: 0];
+    }
+}
+
+- (void) rightMouseDown: (NSEvent*) event
+{
+    __rightMouseDown = true;
+     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    __lx = point.x;
+    __ly = __height - point.y;
+    
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) rightMouseUp: (NSEvent*) event
+{
+    __rightMouseDown = false;
+    NSPoint point = [event locationInWindow];
+    
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) rightMouseDragged: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    if (__rightMouseDown)
+    {
+        // Update the pitch and roll by adding the scaled deltas.
+        __roll += (float)(point.x - __lx) * ACCELEROMETER_FACTOR_X;
+        __pitch -= -(float)(point.y - (__height - __ly)) * ACCELEROMETER_FACTOR_Y;
+    
+        // 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 = (__height - point.y);
+    }
+    
+    // In right-mouse case, whether __rightMouseDown is true or false
+    // this should not matter, mouse move is still occuring
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void)otherMouseDown: (NSEvent*) event 
+{
+    __otherMouseDown = true;
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void)otherMouseUp: (NSEvent*) event 
+{
+    __otherMouseDown = false;
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void)otherMouseDragged: (NSEvent*) event 
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) mouseEntered: (NSEvent*)event
+{
+    __hasMouse = true;
+}
+
+- (void)scrollWheel: (NSEvent*) event 
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
+    [__view->gameLock unlock];
+}
+
+- (void) mouseExited: (NSEvent*)event
+{
+    __leftMouseDown = false;
+    __rightMouseDown = false;
+    __otherMouseDown = false;
+    __hasMouse = false;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return YES;
+}
+
+int getKey(unsigned short keyCode, unsigned int modifierFlags)
+{
+    __shiftDown = (modifierFlags & NSShiftKeyMask);
+    unsigned int caps = (__shiftDown ? 1 : 0) ^ ((modifierFlags & NSAlphaShiftKeyMask) ? 1 : 0);
+    switch(keyCode)
+    {
+        case 0x69:
+            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 caps ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
+        case 0x0B:
+            return caps ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
+        case 0x08:
+            return caps ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
+        case 0x02:
+            return caps ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
+        case 0x0E:
+            return caps ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
+        case 0x03:
+            return caps ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
+        case 0x05:
+            return caps ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
+        case 0x04:
+            return caps ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
+        case 0x22:
+            return caps ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
+        case 0x26:
+            return caps ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
+        case 0x28:
+            return caps ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
+        case 0x25:
+            return caps ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
+        case 0x2E:
+            return caps ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
+        case 0x2D:
+            return caps ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
+        case 0x1F:
+            return caps ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
+        case 0x23:
+            return caps ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
+        case 0x0C:
+            return caps ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
+        case 0x0F:
+            return caps ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
+        case 0x01:
+            return caps ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
+        case 0x11:
+            return caps ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
+        case 0x20:
+            return caps ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
+        case 0x09:
+            return caps ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
+        case 0x0D:
+            return caps ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
+        case 0x07:
+            return caps ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
+        case 0x10:
+            return caps ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case 0x06:
+            return caps ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
+
+        default:
+            return Keyboard::KEY_NONE;
+    }
+}
+
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+int getUnicode(int key)
+{
+    
+    switch (key)
+    {
+        case Keyboard::KEY_BACKSPACE:
+            return 0x0008;
+        case Keyboard::KEY_TAB:
+            return 0x0009;
+        case Keyboard::KEY_RETURN:
+        case Keyboard::KEY_KP_ENTER:
+            return 0x000A;
+        case Keyboard::KEY_ESCAPE:
+            return 0x001B;
+        case Keyboard::KEY_SPACE:
+        case Keyboard::KEY_EXCLAM:
+        case Keyboard::KEY_QUOTE:
+        case Keyboard::KEY_NUMBER:
+        case Keyboard::KEY_DOLLAR:
+        case Keyboard::KEY_PERCENT:
+        case Keyboard::KEY_CIRCUMFLEX:
+        case Keyboard::KEY_AMPERSAND:
+        case Keyboard::KEY_APOSTROPHE:
+        case Keyboard::KEY_LEFT_PARENTHESIS:
+        case Keyboard::KEY_RIGHT_PARENTHESIS:
+        case Keyboard::KEY_ASTERISK:
+        case Keyboard::KEY_PLUS:
+        case Keyboard::KEY_COMMA:
+        case Keyboard::KEY_MINUS:
+        case Keyboard::KEY_PERIOD:
+        case Keyboard::KEY_SLASH:
+        case Keyboard::KEY_ZERO:
+        case Keyboard::KEY_ONE:
+        case Keyboard::KEY_TWO:
+        case Keyboard::KEY_THREE:
+        case Keyboard::KEY_FOUR:
+        case Keyboard::KEY_FIVE:
+        case Keyboard::KEY_SIX:
+        case Keyboard::KEY_SEVEN:
+        case Keyboard::KEY_EIGHT:
+        case Keyboard::KEY_NINE:
+        case Keyboard::KEY_COLON:
+        case Keyboard::KEY_SEMICOLON:
+        case Keyboard::KEY_LESS_THAN:
+        case Keyboard::KEY_EQUAL:
+        case Keyboard::KEY_GREATER_THAN:
+        case Keyboard::KEY_QUESTION:
+        case Keyboard::KEY_AT:
+        case Keyboard::KEY_CAPITAL_A:
+        case Keyboard::KEY_CAPITAL_B:
+        case Keyboard::KEY_CAPITAL_C:
+        case Keyboard::KEY_CAPITAL_D:
+        case Keyboard::KEY_CAPITAL_E:
+        case Keyboard::KEY_CAPITAL_F:
+        case Keyboard::KEY_CAPITAL_G:
+        case Keyboard::KEY_CAPITAL_H:
+        case Keyboard::KEY_CAPITAL_I:
+        case Keyboard::KEY_CAPITAL_J:
+        case Keyboard::KEY_CAPITAL_K:
+        case Keyboard::KEY_CAPITAL_L:
+        case Keyboard::KEY_CAPITAL_M:
+        case Keyboard::KEY_CAPITAL_N:
+        case Keyboard::KEY_CAPITAL_O:
+        case Keyboard::KEY_CAPITAL_P:
+        case Keyboard::KEY_CAPITAL_Q:
+        case Keyboard::KEY_CAPITAL_R:
+        case Keyboard::KEY_CAPITAL_S:
+        case Keyboard::KEY_CAPITAL_T:
+        case Keyboard::KEY_CAPITAL_U:
+        case Keyboard::KEY_CAPITAL_V:
+        case Keyboard::KEY_CAPITAL_W:
+        case Keyboard::KEY_CAPITAL_X:
+        case Keyboard::KEY_CAPITAL_Y:
+        case Keyboard::KEY_CAPITAL_Z:
+        case Keyboard::KEY_LEFT_BRACKET:
+        case Keyboard::KEY_BACK_SLASH:
+        case Keyboard::KEY_RIGHT_BRACKET:
+        case Keyboard::KEY_UNDERSCORE:
+        case Keyboard::KEY_GRAVE:
+        case Keyboard::KEY_A:
+        case Keyboard::KEY_B:
+        case Keyboard::KEY_C:
+        case Keyboard::KEY_D:
+        case Keyboard::KEY_E:
+        case Keyboard::KEY_F:
+        case Keyboard::KEY_G:
+        case Keyboard::KEY_H:
+        case Keyboard::KEY_I:
+        case Keyboard::KEY_J:
+        case Keyboard::KEY_K:
+        case Keyboard::KEY_L:
+        case Keyboard::KEY_M:
+        case Keyboard::KEY_N:
+        case Keyboard::KEY_O:
+        case Keyboard::KEY_P:
+        case Keyboard::KEY_Q:
+        case Keyboard::KEY_R:
+        case Keyboard::KEY_S:
+        case Keyboard::KEY_T:
+        case Keyboard::KEY_U:
+        case Keyboard::KEY_V:
+        case Keyboard::KEY_W:
+        case Keyboard::KEY_X:
+        case Keyboard::KEY_Y:
+        case Keyboard::KEY_Z:
+        case Keyboard::KEY_LEFT_BRACE:
+        case Keyboard::KEY_BAR:
+        case Keyboard::KEY_RIGHT_BRACE:
+        case Keyboard::KEY_TILDE:
+            return key;
+        default:
+            return 0;
+    }
+}
+
+- (void)flagsChanged: (NSEvent*)event
+{
+    unsigned int keyCode = [event keyCode];
+    unsigned int flags = [event modifierFlags];
+
+    [__view->gameLock lock];
+    switch (keyCode) 
+    {
+        case 0x39:
+            gameplay::Platform::keyEventInternal((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
+            break;
+        case 0x38:
+            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
+            break;
+        case 0x3C:
+            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
+            break;
+        case 0x3A:
+            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
+            break;
+        case 0x3D:
+            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
+            break;
+        case 0x3B:
+            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
+            break;
+        case 0x3E:
+            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
+            break;
+        case 0x37:
+            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
+            break;
+        case 0x36:
+            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
+            break;
+    }
+    [__view->gameLock unlock];
+}
+
+- (void) keyDown: (NSEvent*) event
+{
+    if ([event isARepeat] == NO)
+    {
+        int key = getKey([event keyCode], [event modifierFlags]);
+
+        [__view->gameLock lock];
+        gameplay::Platform::keyEventInternal(Keyboard::KEY_PRESS, key);
+        
+        int character = getUnicode(key);
+        if (character)
+        {
+            gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
+        }
+
+        [__view->gameLock unlock];
+    }
+}
+
+- (void) keyUp: (NSEvent*) event
+{
+    [__view->gameLock lock];
+    gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey([event keyCode], [event modifierFlags]));
+    [__view->gameLock unlock];
+}
+
+// Gesture support for Mac OS X Trackpads
+- (bool)isGestureRegistered: (Gesture::GestureEvent) evt
+{
+    return ((_gestureEvents & evt) == evt);
+}
+
+- (void)registerGesture: (Gesture::GestureEvent) evt
+{
+    _gestureEvents |= evt;
+}
+
+- (void)unregisterGesture: (Gesture::GestureEvent) evt
+{
+    _gestureEvents &= (~evt);
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event
+{
+    if([self isGestureRegistered:Gesture::GESTURE_PINCH] == false) return;
+    
+    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny  inView:nil];
+    // Approximate the center by adding and averageing for now
+    // Note this is centroid on the physical device be used for touching, not the display
+    float xavg = 0.0f;
+    float yavg = 0.0f;
+    for(NSTouch *t in touches) {
+        xavg += [t normalizedPosition].x;
+        yavg += [t normalizedPosition].y;
+    }
+    xavg /= [touches count];
+    yavg /= [touches count];
+    
+    [gameLock lock];
+    _game->gesturePinchEvent((int)xavg, (int)yavg, [event magnification]);
+    [gameLock unlock];
+}
+
+
+@end
+
+@interface FullscreenWindow : NSWindow
+{ 
+}
+@end
+
+@implementation FullscreenWindow
+- (BOOL)canBecomeKeyWindow
+{
+    return YES;
+}
+@end
+
+
+namespace gameplay
+{
+
+extern void print(const char* format, ...)
+{
+    GP_ASSERT(format);
+    va_list argptr;
+    va_start(argptr, format);
+    vfprintf(stderr, format, argptr);
+    va_end(argptr);
+}
+    
+Platform::Platform(Game* game)
+: _game(game)
+{
+    __activeGamepads = [[NSMutableDictionary alloc] init];
+    __gamepads = [[NSMutableArray alloc] init];
+    __hidManagerRef = IOHIDManagerCreate(CFAllocatorGetDefault(), kIOHIDOptionsTypeNone);
+    IOHIDManagerRegisterDeviceMatchingCallback(__hidManagerRef, hidDeviceDiscoveredCallback, NULL);
+    IOHIDManagerRegisterDeviceRemovalCallback(__hidManagerRef, hidDeviceRemovalCallback, NULL);
+    
+    CFMutableArrayRef matching = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+    if (matching)
+    {
+        CFDictionaryRef matchingJoystick = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
+        CFDictionaryRef matchingGamepad = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
+        
+        if (matchingJoystick && matchingGamepad)
+        {
+            CFArrayAppendValue(matching, matchingJoystick);
+            CFRelease(matchingJoystick);
+            CFArrayAppendValue(matching, matchingGamepad);
+            CFRelease(matchingGamepad);
+            IOHIDManagerSetDeviceMatchingMultiple(__hidManagerRef, matching);
+        }
+    }
+    
+    IOHIDManagerScheduleWithRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOReturn kr = IOHIDManagerOpen(__hidManagerRef, kIOHIDOptionsTypeNone);
+    assert(kr == 0);
+}
+
+    
+Platform::~Platform()
+{
+    IOHIDManagerUnscheduleFromRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDManagerClose(__hidManagerRef, kIOHIDOptionsTypeNone);
+    
+    CFRelease(__hidManagerRef);
+    __hidManagerRef = NULL;
+    [__activeGamepads release];
+    __activeGamepads = NULL;
+    [__gamepads release];
+    __gamepads = NULL;
+}
+
+    
+Platform* Platform::create(Game* game, void* attachToWindow)
+{
+    __attachToWindow = attachToWindow;
+    Platform* platform = new Platform(game);
+    
+    return platform;
+}
+
+int Platform::enterMessagePump()
+{
+    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
+    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
+    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
+    
+    // Read window settings from config.
+    if (_game->getConfig())
+    {
+        Properties* config = _game->getConfig()->getNamespace("window", true);
+        if (config)
+        {
+            // Read window title.
+            __title = const_cast<char *>(config->getString("title"));
+
+            // Read window size.
+            int width = config->getInt("width");
+            if (width != 0)
+                __width = width;
+            int height = config->getInt("height");
+            if (height != 0)
+                __height = height;
+
+            // Read fullscreen state.
+            __fullscreen = config->getBool("fullscreen");
+            if (__fullscreen && width == 0 && height == 0)
+            {
+                CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
+                __width = CGRectGetWidth(mainMonitor);
+                __height = CGRectGetHeight(mainMonitor);
+            }
+            
+            // Read resizable state.
+            __resizable = config->getBool("resizable");
+        }
+    }
+
+    // Set the scale factors for the mouse movement used to simulate the accelerometer.
+    ACCELEROMETER_FACTOR_X = 90.0f / __width;
+    ACCELEROMETER_FACTOR_Y = 90.0f / __height;
+
+    NSAutoreleasePool* pool = [NSAutoreleasePool new];
+    NSApplication* app = [NSApplication sharedApplication];
+    NSRect screenBounds = [[NSScreen mainScreen] frame];
+    NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
+    
+    __view = [[View alloc] initWithFrame:viewBounds];
+    if (__view == NULL)
+    {
+        GP_ERROR("Failed to create view: exiting.");
+        return EXIT_FAILURE;
+    }
+    
+    NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
+                                 NSMidY(screenBounds) - NSMidY(viewBounds),
+                                 viewBounds.size.width, 
+                                 viewBounds.size.height);
+    
+    NSWindow* window = NULL;
+    if (__fullscreen)
+    {
+        window = [[FullscreenWindow alloc]
+                   initWithContentRect:screenBounds
+                   styleMask:NSBorderlessWindowMask
+                   backing:NSBackingStoreBuffered
+                   defer:NO];
+    }
+    else
+    {
+        window = [[NSWindow alloc]
+                   initWithContentRect:centered
+                  styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
+                   backing:NSBackingStoreBuffered
+                   defer:NO];
+    }
+    
+    [window setAcceptsMouseMovedEvents:YES];
+    [window setContentView:__view];
+    [window setDelegate:__view];
+    [__view release];
+    
+    [app run];
+    
+    [pool release];
+    return EXIT_SUCCESS;
+}
+
+void Platform::signalShutdown() 
+{
+    [__view haltDisplayRenderer];
+
+    // Don't perform terminate right away, enqueue to give game object
+    // a chance to cleanup
+    NSApplication* app = [NSApplication sharedApplication];
+    [app performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
+}
+
+bool Platform::canExit()
+{
+    return true;
+}
+    
+unsigned int Platform::getDisplayWidth()
+{
+    return __width;
+}
+
+unsigned int Platform::getDisplayHeight()
+{
+    return __height;
+}
+
+double Platform::getAbsoluteTime()
+{
+    __timeAbsolute = getMachTimeInMilliseconds();
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(double time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    __vsync = enable;
+    GLint swapInt = enable ? 1 : 0;
+    [[__view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+}
+
+void Platform::swapBuffers()
+{
+    if (__view)
+        CGLFlushDrawable((CGLContextObj)[[__view openGLContext] CGLContextObj]);
+}
+
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
+void Platform::setMultiSampling(bool enabled)
+{
+    if (enabled == __multiSampling)
+    {
+        return;
+    }
+
+    //todo
+
+    __multiSampling = enabled;
+}
+
+bool Platform::isMultiSampling()
+{
+    return __multiSampling;
+}
+
+void Platform::setMultiTouch(bool enabled)
+{
+}
+    
+bool Platform::isMultiTouch()
+{
+    return true;
+}
+    
+void Platform::getAccelerometerValues(float* pitch, float* roll)
+{
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
+    *pitch = __pitch;
+    *roll = __roll;
+}
+
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
+    
+bool Platform::hasMouse()
+{
+    return true;
+}
+
+void Platform::setMouseCaptured(bool captured)
+{
+    if (captured != __mouseCaptured)
+    {
+        if (captured)
+        {
+            [NSCursor hide];
+            __mouseCapturedFirstPass = true;
+        }
+        else
+        {   
+            [NSCursor unhide];
+        }
+        NSWindow* window = __view.window;
+        NSRect rect = window.frame;
+        CGPoint centerPoint;
+        centerPoint.x = rect.origin.x + (rect.size.width / 2);
+        centerPoint.y = rect.origin.y + (rect.size.height / 2);
+        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
+        __mouseCaptured = captured;
+    }
+}
+
+bool Platform::isMouseCaptured()
+{
+    return __mouseCaptured;
+}
+
+void Platform::setCursorVisible(bool visible)
+{
+    if (visible != __cursorVisible)
+    {
+        if (visible)
+        {
+             [NSCursor unhide];
+        }
+        else 
+        {
+             [NSCursor hide];
+        }
+        __cursorVisible = visible;
+    }
+}
+
+bool Platform::isCursorVisible()
+{
+    return __cursorVisible;
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    // Do nothing.
+}
+
+void Platform::shutdownInternal()
+{
+    Game::getInstance()->shutdown();
+}
+
+bool Platform::isGestureSupported(Gesture::GestureEvent evt)
+{
+    // Swipe unsupported as it is considered moving mouse cursor
+    // Two fingers is scrolling
+    // Three fingers is swipe, but is not always enabled on users system
+    // Tap not supported as it is considered a mouse click/button click
+    // on some systems making it difficult to differentiate 
+    switch(evt)
+    {
+        case Gesture::GESTURE_PINCH:
+            return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+void Platform::registerGesture(Gesture::GestureEvent evt)
+{
+    [__view registerGesture:evt];   
+}
+
+void Platform::unregisterGesture(Gesture::GestureEvent evt)
+{
+    [__view unregisterGesture:evt];        
+}
+  
+bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
+{
+     return [__view isGestureRegistered:evt];
+}
+
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+    HIDGamepad* gp = gamepadForGameHandle(gamepad->_handle);
+    if (gp)
+    {
+        // Haven't figured out how to have the triggers not also show up in the buttons array.
+        // So for now a value of -1 means "Don't map this button."
+        static const int PS3Mapping[17] = {
+            Gamepad::BUTTON_MENU1,  // 0x0001
+            Gamepad::BUTTON_L3,     // 0x0002
+            Gamepad::BUTTON_R3,     // 0x0004
+            Gamepad::BUTTON_MENU2,  // 0x0008
+            Gamepad::BUTTON_UP,     // 0x0010
+            Gamepad::BUTTON_RIGHT,  // 0x0020
+            Gamepad::BUTTON_DOWN,   // 0x0040
+            Gamepad::BUTTON_LEFT,   // 0x0080
+            -1,                     // Gamepad::BUTTON_L2,     // 0x0100
+            -1,                     // Gamepad::BUTTON_R2,     // 0x0200
+            Gamepad::BUTTON_L1,     // 0x0400
+            Gamepad::BUTTON_R1,     // 0x0800
+            Gamepad::BUTTON_Y,      // 0x1000
+            Gamepad::BUTTON_B,      // 0x2000
+            Gamepad::BUTTON_A,      // 0x4000
+            Gamepad::BUTTON_X,      // 0x8000
+            Gamepad::BUTTON_MENU3   // 0x10000
+        };
+        
+        static const int XBox360Mapping[20] = {
+            -1, -1, -1, -1, -1,
+            Gamepad::BUTTON_UP,
+            Gamepad::BUTTON_DOWN,
+            Gamepad::BUTTON_LEFT,
+            Gamepad::BUTTON_RIGHT,
+            Gamepad::BUTTON_MENU2,
+            Gamepad::BUTTON_MENU1,
+            Gamepad::BUTTON_L3,
+            Gamepad::BUTTON_R3,
+            Gamepad::BUTTON_L1,
+            Gamepad::BUTTON_R1,
+            Gamepad::BUTTON_MENU3,
+            Gamepad::BUTTON_A,
+            Gamepad::BUTTON_B,
+            Gamepad::BUTTON_X,
+            Gamepad::BUTTON_Y
+        };
+        
+        static const int SteelSeriesFreeMapping[13] = {
+            Gamepad::BUTTON_A,
+            Gamepad::BUTTON_B,
+            -1,
+            Gamepad::BUTTON_X,
+            Gamepad::BUTTON_Y,
+            -1,
+            Gamepad::BUTTON_L1,
+            Gamepad::BUTTON_R1,
+            -1, -1, -1,
+            Gamepad::BUTTON_MENU2,
+            Gamepad::BUTTON_MENU1
+        };
+        
+        static const int GametelMapping103[12] = {
+            Gamepad::BUTTON_B,
+            Gamepad::BUTTON_X,
+            Gamepad::BUTTON_Y,
+            Gamepad::BUTTON_A,
+            Gamepad::BUTTON_L1,
+            Gamepad::BUTTON_R1,
+            Gamepad::BUTTON_MENU1,
+            Gamepad::BUTTON_MENU2,
+            Gamepad::BUTTON_RIGHT,
+            Gamepad::BUTTON_LEFT,
+            Gamepad::BUTTON_DOWN,
+            Gamepad::BUTTON_UP
+        };
+        
+        const int* mapping = NULL;
+        float axisDeadZone = 0.0f;
+        if (gamepad->_vendorId == SONY_USB_VENDOR_ID &&
+            gamepad->_productId == SONY_USB_PS3_PRODUCT_ID)
+        {
+            mapping = PS3Mapping;
+            axisDeadZone = 0.07f;
+        }
+        else if (gamepad->_vendorId == MICROSOFT_VENDOR_ID &&
+                 gamepad->_productId == MICROSOFT_XBOX360_PRODUCT_ID)
+        {
+            mapping = XBox360Mapping;
+            axisDeadZone = 0.2f;
+        }
+        else if (gamepad->_vendorId == STEELSERIES_VENDOR_ID &&
+                 gamepad->_productId == STEELSERIES_FREE_PRODUCT_ID)
+        {
+            mapping = SteelSeriesFreeMapping;
+            axisDeadZone = 0.005f;
+        }
+        else if (gamepad->_vendorId == FRUCTEL_VENDOR_ID &&
+                 gamepad->_productId == FRUCTEL_GAMETEL_PRODUCT_ID)
+        {
+            int ver = [gp versionNumber];
+            int major = ver >> 8;
+            int minor = ver & 0x00ff;
+            if (major >= 1 && minor > 1)
+            {
+                mapping = GametelMapping103;
+            }
+        }
+        
+        unsigned int buttons = 0;
+        for (int i = 0; i < [gp numberOfButtons]; ++i)
+        {
+            HIDGamepadButton* b = [gp buttonAtIndex: i];
+            if ([b state])
+            {
+                // This button is down.
+                if (mapping)
+                {
+                    if (mapping[i] >= 0)
+                        buttons |= (1 << mapping[i]);
+                }
+                else
+                {
+                    buttons |= (1 << i);
+                }
+            }
+        }
+        
+        HIDGamepadAxis* hatSwitch = [gp getHatSwitch];
+        if (hatSwitch != NULL)
+        {
+            CFIndex v = [hatSwitch value];
+            switch (v)
+            {
+                case -1:
+                    break;
+                case 0:
+                    buttons |= (1 << Gamepad::BUTTON_UP);
+                    break;
+                case 1:
+                    buttons |= (1 << Gamepad::BUTTON_UP) | (1 << Gamepad::BUTTON_RIGHT);
+                    break;
+                case 2:
+                    buttons |= (1 << Gamepad::BUTTON_RIGHT);
+                    break;
+                case 3:
+                    buttons |= (1 << Gamepad::BUTTON_RIGHT) | (1 << Gamepad::BUTTON_DOWN);
+                    break;
+                case 4:
+                    buttons |= (1 << Gamepad::BUTTON_DOWN);
+                    break;
+                case 5:
+                    buttons |= (1 << Gamepad::BUTTON_DOWN) | (1 << Gamepad::BUTTON_LEFT);
+                    break;
+                case 6:
+                    buttons |= (1 << Gamepad::BUTTON_LEFT);
+                    break;
+                case 7:
+                    buttons |= (1 << Gamepad::BUTTON_LEFT) | (1 << Gamepad::BUTTON_UP);
+                    break;
+            }
+        }
+        
+        gamepad->setButtons(buttons);
+        
+        for (unsigned int i = 0; i < [gp numberOfSticks]; ++i)
+        {
+            float rawX = [[gp axisAtIndex: i*2] calibratedValue];
+            float rawY = -[[gp axisAtIndex: i*2 + 1] calibratedValue];
+            if (std::fabs(rawX) <= axisDeadZone)
+                rawX = 0;
+            if (std::fabs(rawY) <= axisDeadZone)
+                rawY = 0;
+            
+            gamepad->setJoystickValue(i, rawX, rawY);
+        }
+        
+        for (unsigned int i = 0; i < [gp numberOfTriggerButtons]; ++i)
+        {
+            gamepad->setTriggerValue(i, [[gp triggerButtonAtIndex: i] calibratedStateValue]);
+        }
+    }
+}
+
+}
+
+HIDGamepad* gamepadForLocationID(NSNumber* locationID)
+{
+    HIDGamepad* fgamepad = NULL;
+    for(HIDGamepad* gamepad in __gamepads)
+    {
+        if([[gamepad locationID] isEqual:locationID])
+        {
+            fgamepad = gamepad;
+            break;
+        }
+    }
+    return fgamepad;
+}
+
+HIDGamepad* gamepadForLocationIDValue(unsigned int locationIDValue)
+{
+    return gamepadForLocationID([NSNumber numberWithUnsignedInt:locationIDValue]);
+}
+
+HIDGamepad* gamepadForGameHandle(int gameHandle)
+{
+    HIDGamepad* gamepad = NULL;
+    for(NSNumber* locationID in __activeGamepads)
+    {
+        NSNumber* handleID = [__activeGamepads objectForKey:locationID];
+        if([handleID integerValue] == gameHandle)
+        {
+            gamepad = gamepadForLocationID(locationID);
+            break;
+        }
+    }
+    return gamepad;
+}
+
+CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage) 
+{
+    // create a dictionary to add usage page/usages to
+    CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    if (result) {
+        if (inUsagePage) 
+        {
+            // Add key for device type to refine the matching dictionary.
+            CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
+            if (pageCFNumberRef) 
+            {
+                CFDictionarySetValue(result, CFSTR( kIOHIDDeviceUsagePageKey ), pageCFNumberRef);
+                CFRelease(pageCFNumberRef);
+                
+                // note: the usage is only valid if the usage page is also defined
+                if (inUsage) 
+                {
+                    CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
+                    if (usageCFNumberRef) 
+                    {
+                        CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
+                        CFRelease(usageCFNumberRef);
+                    } 
+                    else 
+                    {
+                        fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__);
+                    }
+                }
+            } 
+            else 
+            {
+                fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__);
+            }
+        }
+    } 
+    else 
+    {
+        fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__);
+    }
+    return result;
+}
+
+CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
+{
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    {
+        return NULL;
+    }
+    return (CFStringRef)typeRef;
+}
+
+int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
+{
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    {
+        return 0;
+    }
+    
+    int value;
+    CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
+    return value;
+}
+
+static void hidDeviceDiscoveredCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
+{
+    CFNumberRef locID = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
+    if(locID)
+    {
+        HIDGamepad* gamepad = [[HIDGamepad alloc] initWithDevice: device];
+        [__gamepads addObject:gamepad];
+    }
+}
+
+static void hidDeviceRemovalCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
+{
+    int removeIndex = -1;
+    NSNumber *locID = (NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
+    if(locID)
+    {
+        for(int i = 0; i < [__gamepads count]; i++)
+        {
+            HIDGamepad* gamepad = [__gamepads objectAtIndex:i];
+            if([[gamepad locationID] isEqual:locID])
+            {
+                removeIndex = i;
+                break;
+            }
+        }
+    }
+    if(removeIndex >= 0)
+    {
+        [__gamepads removeObjectAtIndex:removeIndex];
+    }
+}
+
+static void hidDeviceValueAvailableCallback(void* inContext, IOReturn inResult,  void* inSender)
+{
+    HIDGamepad* d = (HIDGamepad*)inContext;
+    do
+    {
+        IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( ( IOHIDQueueRef ) inSender, 0. );
+        if (!valueRef) break;
+        [d hidValueAvailable:valueRef];
+        CFRelease(valueRef); // Don't forget to release our HID value reference
+    } while (1);
+}
+
+bool Platform::launchURL(const char *url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    CFURLRef urlRef = CFURLCreateWithBytes(
+        NULL,
+        (UInt8*)url,
+        strlen(url),
+        kCFStringEncodingASCII,
+        NULL
+    );
+    const OSStatus err = LSOpenCFURLRef(urlRef, 0);
+    CFRelease(urlRef);
+    return (err == noErr);
+}
+
+#endif

+ 71 - 59
gameplay/src/PlatformWindows.cpp

@@ -36,6 +36,7 @@ static HDC __hdc = 0;
 static HGLRC __hrc = 0;
 static bool __mouseCaptured = false;
 static POINT __mouseCapturePoint = { 0, 0 };
+static bool __multiSampling = false;
 static bool __cursorVisible = true;
 static unsigned int __gamepadsConnected = 0;
 
@@ -43,9 +44,7 @@ static unsigned int __gamepadsConnected = 0;
 static const unsigned int XINPUT_BUTTON_COUNT = 14;
 static const unsigned int XINPUT_JOYSTICK_COUNT = 2;
 static const unsigned int XINPUT_TRIGGER_COUNT = 2;
-#endif
 
-#ifdef USE_XINPUT
 static XINPUT_STATE __xInputState;
 static bool __connectedXInput[4];
 
@@ -501,6 +500,11 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
     case WM_KILLFOCUS:
         break;
+
+    case WM_SIZE:
+        // Window was resized.
+        gameplay::Platform::resizeEventInternal((unsigned int)(short)LOWORD(lParam), (unsigned int)(short)HIWORD(lParam));
+        break;
     }
     
     return DefWindowProc(hwnd, msg, wParam, lParam); 
@@ -515,6 +519,7 @@ struct WindowCreationParams
     RECT rect;
     std::wstring windowName;
     bool fullscreen;
+    bool resizable;
     int samples;
 };
 
@@ -551,6 +556,7 @@ Platform::~Platform()
 bool createWindow(WindowCreationParams* params, HWND* hwnd, HDC* hdc)
 {
     bool fullscreen = false;
+    bool resizable = false;
     RECT rect = { CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT };
     std::wstring windowName;
     if (params)
@@ -558,11 +564,25 @@ bool createWindow(WindowCreationParams* params, HWND* hwnd, HDC* hdc)
         windowName = params->windowName;
         memcpy(&rect, &params->rect, sizeof(RECT));
         fullscreen = params->fullscreen;
+        resizable = params->resizable;
     }
 
     // Set the window style.
-    DWORD style   = (fullscreen ? WS_POPUP : WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
-    DWORD styleEx = (fullscreen ? WS_EX_APPWINDOW : WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
+    DWORD style, styleEx;
+    if (fullscreen)
+    {
+        style = WS_POPUP;
+        styleEx = WS_EX_APPWINDOW;
+    }
+    else
+    {
+        if (resizable)
+            style = WS_OVERLAPPEDWINDOW;
+        else
+            style = WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU;
+        styleEx = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+    }
+    style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
 
     // Adjust the window rectangle so the client size is the requested size.
     AdjustWindowRectEx(&rect, style, FALSE, styleEx);
@@ -663,6 +683,8 @@ bool initializeGL(WindowCreationParams* params)
         WGL_STENCIL_BITS_ARB, DEFAULT_STENCIL_BUFFER_SIZE,
         0
     };
+    __multiSampling = params->samples > 0;
+
     UINT numFormats;
     if (!wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats) || numFormats == 0)
     {
@@ -682,6 +704,8 @@ bool initializeGL(WindowCreationParams* params)
                     break;
                 }
             }
+
+            __multiSampling = params->samples > 0;
         }
 
         if (!valid)
@@ -761,6 +785,7 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     // Read window settings from config.
     WindowCreationParams params;
     params.fullscreen = false;
+    params.resizable = false;
     params.rect.left = 0;
     params.rect.top = 0;
     params.rect.right = 0;
@@ -784,6 +809,8 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
             // Read fullscreen state.
             params.fullscreen = config->getBool("fullscreen");
+            // Read resizable state.
+            params.resizable = config->getBool("resizable");
             // Read multisampling state.
             params.samples = config->getInt("samples");
 
@@ -918,7 +945,6 @@ Platform* Platform::create(Game* game, void* attachToWindow)
                 Platform::gamepadEventConnectedInternal(i, XINPUT_BUTTON_COUNT, XINPUT_JOYSTICK_COUNT, XINPUT_TRIGGER_COUNT, 0, 0, "Microsoft", "XBox360 Controller");
                 __connectedXInput[i] = true;
             }
-
         }
     }
 #endif
@@ -1064,6 +1090,30 @@ void Platform::sleep(long ms)
     Sleep(ms);
 }
 
+void Platform::setMultiSampling(bool enabled)
+{
+    if (enabled == __multiSampling)
+    {
+        return;
+    }
+
+    if (enabled)
+    {
+        glEnable(GL_MULTISAMPLE);
+    }
+    else
+    {
+        glDisable(GL_MULTISAMPLE);
+    }
+
+    __multiSampling = enabled;
+}
+
+bool Platform::isMultiSampling()
+{
+    return __multiSampling;
+}
+
 void Platform::setMultiTouch(bool enabled)
 {
     // not supported
@@ -1083,6 +1133,14 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
     *roll = __roll;
 }
 
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
+
 bool Platform::hasMouse()
 {
     return true;
@@ -1184,13 +1242,15 @@ void Platform::pollGamepadState(Gamepad* gamepad)
         };
 
         const unsigned int *mapping = xInputMapping;
-        for (gamepad->_buttons = 0; buttons; buttons >>= 1, mapping++)
+        unsigned int mappedButtons;
+        for (mappedButtons = 0; buttons; buttons >>= 1, mapping++)
         {
             if (buttons & 1)
             {
-                gamepad->_buttons |= (1 << *mapping);
+                mappedButtons |= (1 << *mapping);
             }
         }
+        gamepad->setButtons(mappedButtons);
 
         unsigned int i;
         for (i = 0; i < gamepad->_joystickCount; ++i)
@@ -1211,7 +1271,7 @@ void Platform::pollGamepadState(Gamepad* gamepad)
                 break;
             }
 
-            gamepad->_joysticks[i].set(x, y);
+            gamepad->setJoystickValue(i, x, y);
         }
 
         for (i = 0; i < gamepad->_triggerCount; ++i)
@@ -1231,67 +1291,19 @@ void Platform::pollGamepadState(Gamepad* gamepad)
 
             if (trigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
             {
-                gamepad->_triggers[i] = 0.0f;
+                gamepad->setTriggerValue(i, 0.0f);
             }
             else
             {
-                gamepad->_triggers[i] = (float)trigger / 255.0f;
+                gamepad->setTriggerValue(i, (float)trigger / 255.0f);
             }
         }
     }
 }
 #else
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-    // TODO: Support generic HID gamepads (including XBox controllers) without requiring XInput.
-}
+void Platform::pollGamepadState(Gamepad* gamepad) { }
 #endif
 
-void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
-        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-        Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
-    }
-}
-
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    if (!Form::keyEventInternal(evt, key))
-    {
-        Game::getInstance()->keyEvent(evt, key);
-        Game::getInstance()->getScriptController()->keyEvent(evt, key);
-    }
-}
-
-bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
-{
-    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else
-    {
-        return Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
-    }
-}
-
-void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-                                             unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
-{
-    Gamepad::add(handle, buttonCount, joystickCount, triggerCount, vendorId, productId, vendorString, productString);
-}
-
-void Platform::gamepadEventDisconnectedInternal(GamepadHandle handle)
-{
-    Gamepad::remove(handle);
-}
-
 void Platform::shutdownInternal()
 {
     Game::getInstance()->shutdown();

+ 23 - 45
gameplay/src/PlatformiOS.mm

@@ -32,6 +32,9 @@ extern const int WINDOW_WIDTH  = [[UIScreen mainScreen] bounds].size.height * [[
 extern const int WINDOW_HEIGHT = [[UIScreen mainScreen] bounds].size.width * [[UIScreen mainScreen] scale];
 extern const int WINDOW_SCALE = [[UIScreen mainScreen] scale];
 
+int __argc = 0;
+char** __argv = 0;
+
 @class AppDelegate;
 @class View;
 
@@ -280,6 +283,8 @@ int getUnicode(int key);
             samples /= 2;
         }
         
+        //todo: __multiSampling = samples > 0;
+
         // Re-bind the default framebuffer
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
         
@@ -1379,6 +1384,14 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
     [__appDelegate getAccelerometerPitch:pitch roll:roll];
 }
+    
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
 
 bool Platform::hasMouse()
 {
@@ -1408,6 +1421,16 @@ bool Platform::isCursorVisible()
     return false;
 }
 
+void Platform::setMultiSampling(bool enabled)
+{
+    //todo
+}
+
+bool Platform::isMultiSampling()
+{
+    return false; //todo
+}
+
 void Platform::setMultiTouch(bool enabled) 
 {
     __view.multipleTouchEnabled = enabled;
@@ -1432,51 +1455,6 @@ void Platform::displayKeyboard(bool display)
         }
     }
 }
-    
-void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
-        Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-        Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
-    }
-}
-    
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    if (!Form::keyEventInternal(evt, key))
-    {
-        Game::getInstance()->keyEvent(evt, key);
-        Game::getInstance()->getScriptController()->keyEvent(evt, key);
-    }
-}
-
-bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
-{
-    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
-    {
-        return true;
-    }
-    else
-    {
-        return Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
-    }
-}
-
-void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
-                                             unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
-{
-    Gamepad::add(handle, buttonCount, joystickCount, triggerCount, vendorId, productId, vendorString, productString);
-}
-
-void Platform::gamepadEventDisconnectedInternal(GamepadHandle handle)
-{
-    Gamepad::remove(handle);
-}
 
 void Platform::shutdownInternal()
 {

+ 24 - 3
gameplay/src/RadioButton.cpp

@@ -81,7 +81,7 @@ const Vector2& RadioButton::getImageSize() const
 
 void RadioButton::addListener(Control::Listener* listener, int eventFlags)
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
         GP_ERROR("TEXT_CHANGED event is not applicable to RadioButton.");
     }
@@ -105,7 +105,7 @@ bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
                     {
                         RadioButton::clearSelected(_groupId);
                         _selected = true;
-                        notifyListeners(Listener::VALUE_CHANGED);
+                        notifyListeners(Control::Listener::VALUE_CHANGED);
                     }
                 }
             }
@@ -116,6 +116,27 @@ bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
     return Button::touchEvent(evt, x, y, contactIndex);
 }
 
+bool RadioButton::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                RadioButton::clearSelected(_groupId);
+                _selected = true;
+                notifyListeners(Control::Listener::VALUE_CHANGED);
+            }
+        }
+        break;
+    }
+
+    return Button::gamepadEvent(evt, gamepad, analogIndex);
+}
+
 void RadioButton::clearSelected(const std::string& groupId)
 {
     std::vector<RadioButton*>::const_iterator it;
@@ -127,7 +148,7 @@ void RadioButton::clearSelected(const std::string& groupId)
         {
             radioButton->_selected = false;
             radioButton->_dirty = true;
-            radioButton->notifyListeners(Listener::VALUE_CHANGED);
+            radioButton->notifyListeners(Control::Listener::VALUE_CHANGED);
         }
     }
 }

+ 7 - 0
gameplay/src/RadioButton.h

@@ -144,6 +144,13 @@ protected:
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.

+ 199 - 70
gameplay/src/RenderState.cpp

@@ -77,6 +77,20 @@ MaterialParameter* RenderState::getParameter(const char* name) const
     return param;
 }
 
+void RenderState::clearParameter(const char* name)
+{
+    for (size_t i = 0, count = _parameters.size(); i < count; ++i)
+    {
+        MaterialParameter* p = _parameters[i];
+        if (p->_name == name)
+        {
+            _parameters.erase(_parameters.begin() + i);
+            SAFE_RELEASE(p);
+            break;
+        }
+    }
+}
+
 /**
  * @script{ignore}
  */
@@ -208,94 +222,186 @@ void RenderState::setNodeBinding(Node* node)
 
 void RenderState::applyAutoBinding(const char* uniformName, const char* autoBinding)
 {
+    GP_ASSERT(_nodeBinding);
+
     MaterialParameter* param = getParameter(uniformName);
     GP_ASSERT(param);
 
+    bool bound = false;
+
     // First attempt to resolve the binding using custom registered resolvers.
     if (_customAutoBindingResolvers.size() > 0)
     {
         for (size_t i = 0, count = _customAutoBindingResolvers.size(); i < count; ++i)
         {
             if (_customAutoBindingResolvers[i](autoBinding, _nodeBinding, param))
-                return; // handled by custom resolver
+            {
+                // Handled by custom auto binding resolver
+                bound = true;
+                break;
+            }
         }
     }
 
     // Perform built-in resolution
-    if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getWorldMatrix);
-    }
-    else if (strcmp(autoBinding, "VIEW_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getViewMatrix);
-    }
-    else if (strcmp(autoBinding, "PROJECTION_MATRIX") == 0)
+    if (!bound)
     {
-        param->bindValue(_nodeBinding, &Node::getProjectionMatrix);
-    }
-    else if (strcmp(autoBinding, "WORLD_VIEW_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getWorldViewMatrix);
-    }
-    else if (strcmp(autoBinding, "VIEW_PROJECTION_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getViewProjectionMatrix);
-    }
-    else if (strcmp(autoBinding, "WORLD_VIEW_PROJECTION_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
-    }
-    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
-    }
-    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
-    }
-    else if (strcmp(autoBinding, "CAMERA_WORLD_POSITION") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
-    }
-    else if (strcmp(autoBinding, "CAMERA_VIEW_POSITION") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
-    }
-    else if (strcmp(autoBinding, "MATRIX_PALETTE") == 0)
-    {
-        Model* model = _nodeBinding->getModel();
-        MeshSkin* skin = model ? model->getSkin() : NULL;
-        if (skin)
+        bound = true;
+
+        if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
         {
-            GP_ASSERT(param);
-            param->bindValue(skin, &MeshSkin::getMatrixPalette, &MeshSkin::getMatrixPaletteSize);
+            param->bindValue(this, &RenderState::autoBindingGetWorldMatrix);
+        }
+        else if (strcmp(autoBinding, "VIEW_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetViewMatrix);
+        }
+        else if (strcmp(autoBinding, "PROJECTION_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetProjectionMatrix);
+        }
+        else if (strcmp(autoBinding, "WORLD_VIEW_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetWorldViewMatrix);
+        }
+        else if (strcmp(autoBinding, "VIEW_PROJECTION_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetViewProjectionMatrix);
+        }
+        else if (strcmp(autoBinding, "WORLD_VIEW_PROJECTION_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetWorldViewProjectionMatrix);
+        }
+        else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetInverseTransposeWorldMatrix);
+        }
+        else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetInverseTransposeWorldViewMatrix);
+        }
+        else if (strcmp(autoBinding, "CAMERA_WORLD_POSITION") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetCameraWorldPosition);
+        }
+        else if (strcmp(autoBinding, "CAMERA_VIEW_POSITION") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetCameraViewPosition);
+        }
+        else if (strcmp(autoBinding, "MATRIX_PALETTE") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetMatrixPalette, &RenderState::autoBindingGetMatrixPaletteSize);
+        }
+        else if (strcmp(autoBinding, "SCENE_AMBIENT_COLOR") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetAmbientColor);
+        }
+        else if (strcmp(autoBinding, "SCENE_LIGHT_COLOR") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetLightColor);
+        }
+        else if (strcmp(autoBinding, "SCENE_LIGHT_DIRECTION") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetLightDirection);
+        }
+        else
+        {
+            bound = false;
+            GP_WARN("Unsupported auto binding type (%d).", autoBinding);
         }
     }
-    else if (strcmp(autoBinding, "SCENE_AMBIENT_COLOR") == 0)
-    {
-        Scene* scene = _nodeBinding->getScene();
-        if (scene)
-            param->bindValue(scene, &Scene::getAmbientColor);
-    }
-    else if (strcmp(autoBinding, "SCENE_LIGHT_COLOR") == 0)
-    {
-        Scene* scene = _nodeBinding->getScene();
-        if (scene)
-            param->bindValue(scene, &Scene::getLightColor);
-    }
-    else if (strcmp(autoBinding, "SCENE_LIGHT_DIRECTION") == 0)
-    {
-        Scene* scene = _nodeBinding->getScene();
-        if (scene)
-            param->bindValue(scene, &Scene::getLightDirection);
-    }
-    else
+
+    if (bound)
     {
-        GP_WARN("Unsupported auto binding type (%d).", autoBinding);
+        // Mark parameter as an auto binding
+        if (param->_type == MaterialParameter::METHOD && param->_value.method)
+            param->_value.method->_autoBinding = true;
     }
 }
 
+const Matrix& RenderState::autoBindingGetWorldMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getWorldMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetViewMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getViewMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetProjectionMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getProjectionMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetWorldViewMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getWorldViewMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetViewProjectionMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getViewProjectionMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetWorldViewProjectionMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getWorldViewProjectionMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetInverseTransposeWorldMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getInverseTransposeWorldMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetInverseTransposeWorldViewMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getInverseTransposeWorldViewMatrix() : Matrix::identity();
+}
+
+Vector3 RenderState::autoBindingGetCameraWorldPosition() const
+{
+    return _nodeBinding ? _nodeBinding->getActiveCameraTranslationWorld() : Vector3::zero();
+}
+
+Vector3 RenderState::autoBindingGetCameraViewPosition() const
+{
+    return _nodeBinding ? _nodeBinding->getActiveCameraTranslationView() : Vector3::zero();
+}
+
+const Vector4* RenderState::autoBindingGetMatrixPalette() const
+{
+    Model* model = _nodeBinding ? _nodeBinding->getModel() : NULL;
+    MeshSkin* skin = model ? model->getSkin() : NULL;
+    return skin ? skin->getMatrixPalette() : NULL;
+}
+
+unsigned int RenderState::autoBindingGetMatrixPaletteSize() const
+{
+    Model* model = _nodeBinding ? _nodeBinding->getModel() : NULL;
+    MeshSkin* skin = model ? model->getSkin() : NULL;
+    return skin ? skin->getMatrixPaletteSize() : 0;
+}
+
+const Vector3& RenderState::autoBindingGetAmbientColor() const
+{
+    Scene* scene = _nodeBinding ? _nodeBinding->getScene() : NULL;
+    return scene ? scene->getAmbientColor() : Vector3::zero();
+}
+
+const Vector3& RenderState::autoBindingGetLightColor() const
+{
+    Scene* scene = _nodeBinding ? _nodeBinding->getScene() : NULL;
+    return scene ? scene->getLightColor() : Vector3::one();
+}
+
+const Vector3& RenderState::autoBindingGetLightDirection() const
+{
+    static Vector3 down(0, -1, 0);
+    Scene* scene = _nodeBinding ? _nodeBinding->getScene() : NULL;
+    return scene ? scene->getLightDirection() : down;
+}
+
 void RenderState::bind(Pass* pass)
 {
     GP_ASSERT(pass);
@@ -359,6 +465,7 @@ void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context)
 {
     GP_ASSERT(renderState);
 
+    // Clone parameters
     for (std::map<std::string, std::string>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
     {
         renderState->setParameterAutoBinding(it->first.c_str(), it->second.c_str());
@@ -368,18 +475,26 @@ void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context)
         const MaterialParameter* param = *it;
         GP_ASSERT(param);
 
+        // If this parameter is a method binding auto binding, don't clone it - it will get setup automatically
+        // via the cloned auto bindings instead.
+        if (param->_type == MaterialParameter::METHOD && param->_value.method && param->_value.method->_autoBinding)
+            continue;
+
         MaterialParameter* paramCopy = new MaterialParameter(param->getName());
         param->cloneInto(paramCopy);
 
         renderState->_parameters.push_back(paramCopy);
     }
-    renderState->_parent = _parent;
+
+    // Clone our state block
     if (_state)
     {
-        renderState->setStateBlock(_state);
+        _state->cloneInto(renderState->getStateBlock());
     }
 
-    // Note that _nodeBinding is not set here, it should be set by the caller.
+    // Notes:
+    // 1. _nodeBinding should not be set here, it should be set by the caller.
+    // 2. _parent should not be set here, since it's set in the constructor of Technique and Pass.
 }
 
 RenderState::StateBlock::StateBlock()
@@ -529,6 +644,20 @@ void RenderState::StateBlock::enableDepthWrite()
     }
 }
 
+void RenderState::StateBlock::cloneInto(StateBlock* state)
+{
+    GP_ASSERT(state);
+
+    state->_cullFaceEnabled = _cullFaceEnabled;
+    state->_depthTestEnabled = _depthTestEnabled;
+    state->_depthWriteEnabled = _depthWriteEnabled;
+    state->_depthFunction = _depthFunction;
+    state->_blendEnabled = _blendEnabled;
+    state->_blendSrc = _blendSrc;
+    state->_blendDst = _blendDst;
+    state->_bits = _bits;
+}
+
 static bool parseBoolean(const char* value)
 {
     GP_ASSERT(value);

+ 34 - 0
gameplay/src/RenderState.h

@@ -2,6 +2,8 @@
 #define RENDERSTATE_H_
 
 #include "Ref.h"
+#include "Vector3.h"
+#include "Vector4.h"
 
 namespace gameplay
 {
@@ -285,6 +287,8 @@ public:
 
         static void enableDepthWrite();
 
+        void cloneInto(StateBlock* state);
+
         // States
         bool _cullFaceEnabled;
         bool _depthTestEnabled;
@@ -304,12 +308,25 @@ public:
      * The returned MaterialParameter can be used to set values for the specified
      * parameter name.
      *
+     * Note that this method causes a new MaterialParameter to be created if one
+     * does not already exist for the given parameter name.
+     *
      * @param name Material parameter (uniform) name.
      * 
      * @return A MaterialParameter for the specified name.
      */
     MaterialParameter* getParameter(const char* name) const;
 
+    /**
+     * Clears the MaterialParameter with the given name.
+     *
+     * If a material parameter exists for the given name, it is destroyed and
+     * removed from this RenderState.
+     *
+     * @param name Material parameter (uniform) name.
+     */
+    void clearParameter(const char* name);
+
     /**
      * Sets a material parameter auto-binding.
      *
@@ -461,6 +478,23 @@ private:
      */
     RenderState& operator=(const RenderState&);
 
+    // Internal auto binding handler methods.
+    const Matrix& autoBindingGetWorldMatrix() const;
+    const Matrix& autoBindingGetViewMatrix() const;
+    const Matrix& autoBindingGetProjectionMatrix() const;
+    const Matrix& autoBindingGetWorldViewMatrix() const;
+    const Matrix& autoBindingGetViewProjectionMatrix() const;
+    const Matrix& autoBindingGetWorldViewProjectionMatrix() const;
+    const Matrix& autoBindingGetInverseTransposeWorldMatrix() const;
+    const Matrix& autoBindingGetInverseTransposeWorldViewMatrix() const;
+    Vector3 autoBindingGetCameraWorldPosition() const;
+    Vector3 autoBindingGetCameraViewPosition() const;
+    const Vector4* autoBindingGetMatrixPalette() const;
+    unsigned int autoBindingGetMatrixPaletteSize() const;
+    const Vector3& autoBindingGetAmbientColor() const;
+    const Vector3& autoBindingGetLightColor() const;
+    const Vector3& autoBindingGetLightDirection() const;
+
 protected:
 
     /**

+ 82 - 0
gameplay/src/Scene.cpp

@@ -5,6 +5,7 @@
 #include "MeshSkin.h"
 #include "Joint.h"
 #include "Terrain.h"
+#include "Bundle.h"
 
 namespace gameplay
 {
@@ -12,6 +13,52 @@ namespace gameplay
 // Global list of active scenes
 static std::vector<Scene*> __sceneList;
 
+static inline char lowercase(char c)
+{
+    if (c >= 'A' && c <='Z')
+    {
+        c |= 0x20;
+    }
+    return c;
+}
+
+// Returns true if 'str' ends with 'suffix'; false otherwise.
+static bool endsWith(const char* str, const char* suffix, bool ignoreCase)
+{
+    if (str == NULL || suffix == NULL)
+        return false;
+    size_t length = strlen(str);
+    size_t suffixLength = strlen(suffix);
+
+    if (suffixLength > length)
+    {
+        return false;
+    }
+
+    size_t offset = length - suffixLength;
+
+    const char* p = str + offset;
+    while (*p != '\0' && *suffix != '\0')
+    {
+        if (ignoreCase)
+        {
+            if (lowercase(*p) != lowercase(*suffix))
+            {
+                return false;
+            }
+        }
+        else if (*p != *suffix)
+        {
+            return false;
+        }
+        
+        ++p;
+        ++suffix;
+    }
+    return true;
+}
+
+
 Scene::Scene(const char* id)
     : _id(id ? id : ""), _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), 
     _lightColor(1,1,1), _lightDirection(0,-1,0), _bindAudioListenerToCamera(true), _debugBatch(NULL)
@@ -50,6 +97,17 @@ Scene* Scene::create(const char* id)
 
 Scene* Scene::load(const char* filePath)
 {
+    if (endsWith(filePath, ".gpb", true))
+    {
+        Scene* scene = NULL;
+        Bundle* bundle = Bundle::create(filePath);
+        if (bundle)
+        {
+            scene = bundle->loadScene();
+            SAFE_RELEASE(bundle);
+        }
+        return scene;
+    }
     return SceneLoader::load(filePath);
 }
 
@@ -136,6 +194,30 @@ unsigned int Scene::findNodes(const char* id, std::vector<Node*>& nodes, bool re
     return count;
 }
 
+void Scene::visitNode(Node* node, const char* visitMethod)
+{
+    ScriptController* sc = Game::getInstance()->getScriptController();
+
+    // Invoke the visit method for this node.
+    if (!sc->executeFunction<bool>(visitMethod, "<Node>", node))
+        return;
+
+    // If this node has a model with a mesh skin, visit the joint hierarchy within it
+    // since we don't add joint hierarcies directly to the scene. If joints are never
+    // visited, it's possible that nodes embedded within the joint hierarchy that contain
+    // models will never get visited (and therefore never get drawn).
+    if (node->_model && node->_model->_skin && node->_model->_skin->_rootNode)
+    {
+        visitNode(node->_model->_skin->_rootNode, visitMethod);
+    }
+
+    // Recurse for all children.
+    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
+    {
+        visitNode(child, visitMethod);
+    }
+}
+
 Node* Scene::addNode(const char* id)
 {
     Node* node = Node::create(id);

+ 19 - 17
gameplay/src/Scene.h

@@ -36,9 +36,9 @@ public:
     static Scene* create(const char* id = NULL);
 
     /**
-     * Loads a scene from the given '.scene' file.
+     * Loads a scene from the given '.scene' or '.gpb' file.
      * 
-     * @param filePath The path to the '.scene' file to load from.
+     * @param filePath The path to the '.scene' or '.gpb' file to load from.
      * @return The loaded scene or <code>NULL</code> if the scene
      *      could not be loaded from the given file.
      * @script{create}
@@ -333,7 +333,7 @@ private:
     /**
      * Visits the given node and all of its children recursively.
      */
-    inline void visitNode(Node* node, const char* visitMethod);
+    void visitNode(Node* node, const char* visitMethod);
 
     std::string _id;
     Camera* _activeCamera;
@@ -356,7 +356,6 @@ void Scene::visit(T* instance, bool (T::*visitMethod)(Node*))
     }
 }
 
-
 template <class T, class C>
 void Scene::visit(T* instance, bool (T::*visitMethod)(Node*,C), C cookie)
 {
@@ -381,6 +380,15 @@ void Scene::visitNode(Node* node, T* instance, bool (T::*visitMethod)(Node*))
     if (!(instance->*visitMethod)(node))
         return;
 
+    // If this node has a model with a mesh skin, visit the joint hierarchy within it
+    // since we don't add joint hierarcies directly to the scene. If joints are never
+    // visited, it's possible that nodes embedded within the joint hierarchy that contain
+    // models will never get visited (and therefore never get drawn).
+    if (node->_model && node->_model->_skin && node->_model->_skin->_rootNode)
+    {
+        visitNode(node->_model->_skin->_rootNode, instance, visitMethod);
+    }
+
     // Recurse for all children.
     for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
     {
@@ -395,25 +403,19 @@ void Scene::visitNode(Node* node, T* instance, bool (T::*visitMethod)(Node*,C),
     if (!(instance->*visitMethod)(node, cookie))
         return;
 
-    // Recurse for all children.
-    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
+    // If this node has a model with a mesh skin, visit the joint hierarchy within it
+    // since we don't add joint hierarcies directly to the scene. If joints are never
+    // visited, it's possible that nodes embedded within the joint hierarchy that contain
+    // models will never get visited (and therefore never get drawn).
+    if (node->_model && node->_model->_skin && node->_model->_skin->_rootNode)
     {
-        visitNode(child, instance, visitMethod, cookie);
+        visitNode(node->_model->_skin->_rootNode, instance, visitMethod, cookie);
     }
-}
-
-inline void Scene::visitNode(Node* node, const char* visitMethod)
-{
-    ScriptController* sc = Game::getInstance()->getScriptController();
-
-    // Invoke the visit method for this node.
-    if (!sc->executeFunction<bool>(visitMethod, "<Node>", node))
-        return;
 
     // Recurse for all children.
     for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
     {
-        visitNode(child, visitMethod);
+        visitNode(child, instance, visitMethod, cookie);
     }
 }
 

+ 20 - 0
gameplay/src/SceneLoader.cpp

@@ -4,6 +4,7 @@
 #include "Bundle.h"
 #include "SceneLoader.h"
 #include "Terrain.h"
+#include "Light.h"
 
 namespace gameplay
 {
@@ -81,6 +82,7 @@ Scene* SceneLoader::loadInternal(const char* url)
         SceneNodeProperty::MATERIAL | 
         SceneNodeProperty::PARTICLE |
         SceneNodeProperty::TERRAIN |
+        SceneNodeProperty::LIGHT |
         SceneNodeProperty::CAMERA |
         SceneNodeProperty::ROTATE |
         SceneNodeProperty::SCALE |
@@ -211,6 +213,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::PARTICLE ||
         snp._type == SceneNodeProperty::TERRAIN ||
+        snp._type == SceneNodeProperty::LIGHT ||
         snp._type == SceneNodeProperty::CAMERA ||
         snp._type == SceneNodeProperty::COLLISION_OBJECT)
     {
@@ -261,6 +264,13 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             SAFE_RELEASE(terrain);
             break;
         }
+        case SceneNodeProperty::LIGHT:
+        {
+            Light* light = Light::create(p);
+            node->setLight(light);
+            SAFE_RELEASE(light);
+            break;
+        }
         case SceneNodeProperty::CAMERA:
         {
             Camera* camera = Camera::create(p);
@@ -575,6 +585,12 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::TERRAIN, propertyUrl.c_str());
                     _properties[propertyUrl] = subns;
                 }
+                else if (strcmp(subns->getNamespace(), "light") == 0)
+                {
+                    propertyUrl += "light/" + std::string(subns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::LIGHT, propertyUrl.c_str());
+                    _properties[propertyUrl] = subns;
+                }
                 else if (strcmp(subns->getNamespace(), "camera") == 0)
                 {
                     propertyUrl += "camera/" + std::string(subns->getId());
@@ -631,6 +647,10 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::TERRAIN, ns->getString());
                 }
+                else if (strcmp(name, "light") == 0)
+                {
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::LIGHT, ns->getString());
+                }
                 else if (strcmp(name, "camera") == 0)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::CAMERA, ns->getString());

+ 7 - 6
gameplay/src/SceneLoader.h

@@ -50,12 +50,13 @@ private:
             MATERIAL = 2,
             PARTICLE = 4,
             TERRAIN = 8,
-            CAMERA = 16,
-            COLLISION_OBJECT = 32,
-            TRANSLATE = 64,
-            ROTATE = 128,
-            SCALE = 256,
-            URL = 512
+            LIGHT = 16,
+            CAMERA = 32,
+            COLLISION_OBJECT = 64,
+            TRANSLATE = 128,
+            ROTATE = 256,
+            SCALE = 512,
+            URL = 1024
         };
 
         SceneNodeProperty(Type type, const std::string& url, int index);

+ 243 - 78
gameplay/src/ScriptController.cpp

@@ -41,6 +41,92 @@
     \
     return arr
 
+#define PUSH_NESTED_VARIABLE(name, defaultValue) \
+    int top = lua_gettop(_lua); \
+    if (!getNestedVariable(_lua, (name))) \
+    { \
+        lua_settop(_lua, top); \
+        return (defaultValue); \
+    }
+
+#define POP_NESTED_VARIABLE() \
+    lua_settop(_lua, top)
+
+/**
+ * Pushes onto the stack, the value of the global 'name' or the nested table value if 'name' is a '.' separated 
+ * list of tables of the form "A.B.C.D", where A, B and C are tables and D is a variable name in the table C.
+ * 
+ * If 'name' does not contain any '.' then it is assumed to be the name of a global variable.
+ * 
+ * This function will not restore the stack if there is an error.
+ * 
+ * @param lua  The Lua state.
+ * @param name The name of a global variable or a '.' separated list of nested tables ending with a variable name.
+ *             The name value may be in the format "A.B.C.D" where A is a table and B, C are child tables.
+ *             D is any type, which will be accessed by the calling function.
+ * 
+ * @return True if the tables were pushed on the stack or the global variable was pushed. Returns false on error.
+ */
+static bool getNestedVariable(lua_State* lua, const char* name)
+{
+    if (strchr(name, '.') == NULL)
+    {
+        lua_getglobal(lua, name);
+        return true;
+    }
+    static std::string str;
+    // Copy the input string to a std::string so we can modify it because 
+    // some of the Lua functions require NULL terminated c-strings.
+    str.assign(name);
+
+    // Find the first table, which will be a global variable.
+    char* start = const_cast<char*>(str.c_str());
+    char* end = strchr(start, '.');
+    if (end == NULL)
+    {
+        return false;
+    }
+    ++end;
+    *(end - 1) = '\0';
+    lua_getglobal(lua, start);
+    *(end - 1) = '.';
+    if (!lua_istable(lua, -1))
+    {
+        return false;
+    }
+    // Push the nested tables
+    for (;;)
+    {
+        start = end;
+        end = strchr(start, '.');
+        if (end == '\0' || end == NULL)
+        {
+            // push the last variable
+            lua_pushstring(lua, start);
+            lua_gettable(lua, -2);
+            return true;
+        }
+        else
+        {
+            // Push the next table
+            *end = '\0';
+            lua_pushstring(lua, start);
+            *end = '.';
+            lua_gettable(lua, -2);
+            if (!lua_istable(lua, -1))
+            {
+                return false;
+            }
+            ++end;
+            if (*end == '.')
+            {
+                return false;
+            }
+        }
+    }
+    return false;
+}
+
 namespace gameplay
 {
 
@@ -340,17 +426,33 @@ bool ScriptUtil::luaCheckBool(lua_State* state, int n)
 
 void ScriptController::loadScript(const char* path, bool forceReload)
 {
+    GP_ASSERT(path);
     std::set<std::string>::iterator iter = _loadedScripts.find(path);
     if (iter == _loadedScripts.end() || forceReload)
     {
+#ifdef __ANDROID__
         const char* scriptContents = FileSystem::readAll(path);
         if (luaL_dostring(_lua, scriptContents))
+        {
             GP_WARN("Failed to run Lua script with error: '%s'.", lua_tostring(_lua, -1));
-
+        }
         SAFE_DELETE_ARRAY(scriptContents);
-
+#else
+        std::string fullPath;
+        if (!FileSystem::isAbsolutePath(path))
+        {
+            fullPath.append(FileSystem::getResourcePath());
+        }
+        fullPath.append(path);
+        if (luaL_dofile(_lua, fullPath.c_str()))
+        {
+            GP_WARN("Failed to run Lua script with error: '%s'.", lua_tostring(_lua, -1));
+        }
+#endif
         if (iter == _loadedScripts.end())
+        {
             _loadedScripts.insert(path);
+        }
     }
 }
 
@@ -377,97 +479,97 @@ std::string ScriptController::loadUrl(const char* url)
 
 bool ScriptController::getBool(const char* name, bool defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     bool b = lua_isboolean(_lua, -1) ? ScriptUtil::luaCheckBool(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return b;
 }
 
 char ScriptController::getChar(const char* name, char defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     char c = lua_isnumber(_lua, -1) ?  (char)luaL_checkint(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return c;
 }
 
 short ScriptController::getShort(const char* name, short defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     short n = lua_isnumber(_lua, -1) ? (short)luaL_checkint(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
 }
 
 int ScriptController::getInt(const char* name, int defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     int n = lua_isnumber(_lua, -1) ? luaL_checkint(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
 }
 
 long ScriptController::getLong(const char* name, long defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     long n = lua_isnumber(_lua, -1) ? luaL_checklong(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
 }
 
 unsigned char ScriptController::getUnsignedChar(const char* name, unsigned char defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     unsigned char c = lua_isnumber(_lua, -1) ? (unsigned char)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return c;
 }
 
 unsigned short ScriptController::getUnsignedShort(const char* name, unsigned short defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     unsigned short n = lua_isnumber(_lua, -1) ? (unsigned short)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
 }
 
 unsigned int ScriptController::getUnsignedInt(const char* name, unsigned int defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     unsigned int n = lua_isnumber(_lua, -1) ? (unsigned int)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
 }
 
 unsigned long ScriptController::getUnsignedLong(const char* name, unsigned long defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     unsigned long n = lua_isnumber(_lua, -1) ? (unsigned long)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
 }
 
 float ScriptController::getFloat(const char* name, float defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     float f = lua_isnumber(_lua, -1) ? (float)luaL_checknumber(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return f;
 }
 
 double ScriptController::getDouble(const char* name, double defaultValue)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, defaultValue);
     double n = lua_isnumber(_lua, -1) ? (double)luaL_checknumber(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
 }
 
 const char* ScriptController::getString(const char* name)
 {
-    lua_getglobal(_lua, name);
+    PUSH_NESTED_VARIABLE(name, NULL);
     const char* s = lua_isstring(_lua, -1) ? luaL_checkstring(_lua, -1) : NULL;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return s;
 }
 
@@ -555,15 +657,10 @@ void ScriptController::print(const char* str1, const char* str2)
 
 ScriptController::ScriptController() : _lua(NULL)
 {
-    memset(_callbacks, 0, sizeof(std::string*) * CALLBACK_COUNT);
 }
 
 ScriptController::~ScriptController()
 {
-    for (unsigned int i = 0; i < CALLBACK_COUNT; i++)
-    {
-        SAFE_DELETE(_callbacks[i]);
-    }
 }
 
 static const char* lua_print_function = 
@@ -571,7 +668,6 @@ static const char* lua_print_function =
     "    ScriptController.print(table.concat({...},\"\\t\"), \"\\n\")\n"
     "end\n";
 
-#ifndef WIN32 
 static const char* lua_loadfile_function = 
     "do\n"
     "    local oldLoadfile = loadfile\n"
@@ -595,7 +691,6 @@ static const char* lua_dofile_function =
     "        return oldDofile(filename)\n"
     "    end\n"
     "end\n";
-#endif
 
 void ScriptController::initialize()
 {
@@ -606,27 +701,25 @@ void ScriptController::initialize()
 
 #ifndef NO_LUA_BINDINGS
     lua_RegisterAllBindings();
+    ScriptUtil::registerFunction("convert", ScriptController::convert);
 #endif
 
     // Create our own print() function that uses gameplay::print.
     if (luaL_dostring(_lua, lua_print_function))
         GP_ERROR("Failed to load custom print() function with error: '%s'.", lua_tostring(_lua, -1));
 
-#ifndef WIN32
     // Change the functions that read a file to use FileSystem.getResourcePath as their base path.
     if (luaL_dostring(_lua, lua_loadfile_function))
         GP_ERROR("Failed to load custom loadfile() function with error: '%s'.", lua_tostring(_lua, -1));
     if (luaL_dostring(_lua, lua_dofile_function))
         GP_ERROR("Failed to load custom dofile() function with error: '%s'.", lua_tostring(_lua, -1));
-#endif
 }
 
 void ScriptController::initializeGame()
 {
-    if (_callbacks[INITIALIZE])
-    {
-        executeFunction<void>(_callbacks[INITIALIZE]->c_str());
-    }
+    std::vector<std::string>& list = _callbacks[INITIALIZE];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
+        executeFunction<void>(list[i].c_str());
 }
 
 void ScriptController::finalize()
@@ -640,21 +733,15 @@ void ScriptController::finalize()
 
 void ScriptController::finalizeGame()
 {
-	std::string finalizeCallback;
-	if (_callbacks[FINALIZE])
-		finalizeCallback = _callbacks[FINALIZE]->c_str();
+    std::vector<std::string> finalizeCallbacks = _callbacks[FINALIZE]; // no & : makes a copy of the vector
 
 	// Remove any registered callbacks so they don't get called after shutdown
 	for (unsigned int i = 0; i < CALLBACK_COUNT; i++)
-    {
-        SAFE_DELETE(_callbacks[i]);
-    }
+        _callbacks[i].clear();
 
-	// Fire script finalize callback
-    if (!finalizeCallback.empty())
-	{
-        executeFunction<void>(finalizeCallback.c_str());
-    }
+	// Fire script finalize callbacks
+    for (size_t i = 0, count = finalizeCallbacks.size(); i < count; ++i)
+        executeFunction<void>(finalizeCallbacks[i].c_str());
 
     // Perform a full garbage collection cycle.
 	// Note that this does NOT free any global variables declared in scripts, since 
@@ -665,51 +752,55 @@ void ScriptController::finalizeGame()
 
 void ScriptController::update(float elapsedTime)
 {
-    if (_callbacks[UPDATE])
-    {
-        executeFunction<void>(_callbacks[UPDATE]->c_str(), "f", elapsedTime);
-    }
+    std::vector<std::string>& list = _callbacks[UPDATE];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
+        executeFunction<void>(list[i].c_str(), "f", elapsedTime);
 }
 
 void ScriptController::render(float elapsedTime)
 {
-    if (_callbacks[RENDER])
-    {
-        executeFunction<void>(_callbacks[RENDER]->c_str(), "f", elapsedTime);
-    }
+    std::vector<std::string>& list = _callbacks[RENDER];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
+        executeFunction<void>(list[i].c_str(), "f", elapsedTime);
+}
+
+void ScriptController::resizeEvent(unsigned int width, unsigned int height)
+{
+    std::vector<std::string>& list = _callbacks[RESIZE_EVENT];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
+        executeFunction<void>(list[i].c_str(), "uiui", width, height);
 }
 
 void ScriptController::keyEvent(Keyboard::KeyEvent evt, int key)
 {
-    if (_callbacks[KEY_EVENT])
-    {
-        executeFunction<void>(_callbacks[KEY_EVENT]->c_str(), "[Keyboard::KeyEvent][Keyboard::Key]", evt, key);
-    }
+    std::vector<std::string>& list = _callbacks[KEY_EVENT];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
+        executeFunction<void>(list[i].c_str(), "[Keyboard::KeyEvent][Keyboard::Key]", evt, key);
 }
 
 void ScriptController::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-    if (_callbacks[TOUCH_EVENT])
-    {
-        executeFunction<void>(_callbacks[TOUCH_EVENT]->c_str(), "[Touch::TouchEvent]iiui", evt, x, y, contactIndex);
-    }
+    std::vector<std::string>& list = _callbacks[TOUCH_EVENT];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
+        executeFunction<void>(list[i].c_str(), "[Touch::TouchEvent]iiui", evt, x, y, contactIndex);
 }
 
 bool ScriptController::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 {
-    if (_callbacks[MOUSE_EVENT])
+    std::vector<std::string>& list = _callbacks[MOUSE_EVENT];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
     {
-        return executeFunction<bool>(_callbacks[MOUSE_EVENT]->c_str(), "[Mouse::MouseEvent]iiii", evt, x, y, wheelDelta);
+        if (executeFunction<bool>(list[i].c_str(), "[Mouse::MouseEvent]iii", evt, x, y, wheelDelta))
+            return true;
     }
     return false;
 }
 
-void ScriptController::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
+void ScriptController::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
-    if (_callbacks[GAMEPAD_EVENT])
-    {
-        executeFunction<void>(_callbacks[GAMEPAD_EVENT]->c_str(), "[Gamepad::GamepadEvent]<Gamepad>", evt, gamepad);
-    }
+    std::vector<std::string>& list = _callbacks[GAMEPAD_EVENT];
+    for (size_t i = 0, count = list.size(); i < count; ++i)
+        executeFunction<void>(list[i].c_str(), "[Gamepad::GamepadEvent]<Gamepad>", evt, gamepad);
 }
 
 void ScriptController::executeFunctionHelper(int resultCount, const char* func, const char* args, va_list* list)
@@ -723,10 +814,14 @@ void ScriptController::executeFunctionHelper(int resultCount, const char* func,
         return;
     }
 
-    const char* sig = args;
+    if (!getNestedVariable(_lua, func))
+    {
+        GP_WARN("Failed to call function '%s'", func);
+        return;
+    }
 
+    const char* sig = args;
     int argumentCount = 0;
-    lua_getglobal(_lua, func);
 
     // Push the arguments to the Lua stack if there are any.
     if (sig)
@@ -836,10 +931,33 @@ void ScriptController::executeFunctionHelper(int resultCount, const char* func,
         GP_WARN("Failed to call function '%s' with error '%s'.", func, lua_tostring(_lua, -1));
 }
 
-void ScriptController::registerCallback(ScriptCallback callback, const std::string& function)
+void ScriptController::registerCallback(const char* callback, const char* function)
 {
-    SAFE_DELETE(_callbacks[callback]);
-    _callbacks[callback] = new std::string(function);
+    ScriptCallback scb = toCallback(callback);
+    if (scb < INVALID_CALLBACK)
+    {
+        _callbacks[scb].push_back(function);
+    }
+    else
+    {
+        GP_WARN("Invalid script callback function specified: %s", callback);
+    }
+}
+
+void ScriptController::unregisterCallback(const char* callback, const char* function)
+{
+    ScriptCallback scb = toCallback(callback);
+    if (scb < INVALID_CALLBACK)
+    {
+        std::vector<std::string>& list = _callbacks[scb];
+        std::vector<std::string>::iterator itr = std::find(list.begin(), list.end(), std::string(function));
+        if (itr != list.end())
+            list.erase(itr);
+    }
+    else
+    {
+        GP_WARN("Invalid script callback function specified: %s", callback);
+    }
 }
 
 ScriptController::ScriptCallback ScriptController::toCallback(const char* name)
@@ -852,6 +970,8 @@ ScriptController::ScriptCallback ScriptController::toCallback(const char* name)
         return ScriptController::RENDER;
     else if (strcmp(name, "finalize") == 0)
         return ScriptController::FINALIZE;
+    else if (strcmp(name, "resizeEvent") == 0)
+        return ScriptController::RESIZE_EVENT;
     else if (strcmp(name, "keyEvent") == 0)
         return ScriptController::KEY_EVENT;
     else if (strcmp(name, "touchEvent") == 0)
@@ -864,31 +984,74 @@ ScriptController::ScriptCallback ScriptController::toCallback(const char* name)
         return ScriptController::INVALID_CALLBACK;
 }
 
+int ScriptController::convert(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if (lua_type(state, 1) == LUA_TUSERDATA && lua_type(state, 2) == LUA_TSTRING )
+            {
+                // Get parameter 2
+                const char* param2 = ScriptUtil::getString(2, false);
+                if (param2 != NULL)
+                {
+                    luaL_getmetatable(state, param2);
+                    lua_setmetatable(state, -3);
+                }
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_convert - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 // Helper macros.
 #define SCRIPT_EXECUTE_FUNCTION_NO_PARAM(type, checkfunc) \
+    int top = lua_gettop(_lua); \
     executeFunctionHelper(1, func, NULL, NULL); \
     type value = (type)checkfunc(_lua, -1); \
     lua_pop(_lua, -1); \
+    lua_settop(_lua, top); \
     return value;
 
 #define SCRIPT_EXECUTE_FUNCTION_PARAM(type, checkfunc) \
+    int top = lua_gettop(_lua); \
     va_list list; \
     va_start(list, args); \
     executeFunctionHelper(1, func, args, &list); \
     type value = (type)checkfunc(_lua, -1); \
     lua_pop(_lua, -1); \
     va_end(list); \
+    lua_settop(_lua, top); \
     return value;
 
 #define SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(type, checkfunc) \
+    int top = lua_gettop(_lua); \
     executeFunctionHelper(1, func, args, list); \
     type value = (type)checkfunc(_lua, -1); \
     lua_pop(_lua, -1); \
+    lua_settop(_lua, top); \
     return value;
 
 template<> void ScriptController::executeFunction<void>(const char* func)
 {
+    int top = lua_gettop(_lua);
     executeFunctionHelper(0, func, NULL, NULL);
+    lua_settop(_lua, top);
 }
 
 template<> bool ScriptController::executeFunction<bool>(const char* func)
@@ -954,10 +1117,12 @@ template<> std::string ScriptController::executeFunction<std::string>(const char
 /** Template specialization. */
 template<> void ScriptController::executeFunction<void>(const char* func, const char* args, ...)
 {
+    int top = lua_gettop(_lua);
     va_list list;
     va_start(list, args);
     executeFunctionHelper(0, func, args, &list);
     va_end(list);
+    lua_settop(_lua, top);
 }
 
 /** Template specialization. */

Some files were not shown because too many files changed in this diff