Переглянути джерело

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 роки тому
батько
коміт
5d8b1a2f6b
100 змінених файлів з 10660 додано та 11105 видалено
  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-Profile
 /Device-Release
 /Device-Release
 /gameplay-internal
 /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/Debug
 /gameplay/DebugMem
 /gameplay/DebugMem
@@ -45,8 +37,6 @@ Thumbs.db
 /gameplay/Device-Coverage
 /gameplay/Device-Coverage
 /gameplay/Device-Profile
 /gameplay/Device-Profile
 /gameplay/Device-Release
 /gameplay/Device-Release
-/gameplay.xcworkspace/xcuserdata
-/gameplay/gameplay.xcodeproj/xcuserdata
 /gameplay/windows
 /gameplay/windows
 /gameplay/android/NUL
 /gameplay/android/NUL
 /gameplay/android/proguard.cfg
 /gameplay/android/proguard.cfg
@@ -54,208 +44,219 @@ Thumbs.db
 /gameplay/android/local.properties
 /gameplay/android/local.properties
 /gameplay/android/project.properties
 /gameplay/android/project.properties
 /gameplay/android/obj
 /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
 ## v1.6.0
 
 
 - Adds file Stream interface for reading/writing files instead of using fread/fwrite. 
 - 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)
 add_subdirectory(gameplay)
 
 
 # gameplay samples
 # 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 
 # 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.
 # 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 
 # 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 
 # 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 
 # have to redo this when upgrading to a newer version of doxygen or when 
 # changing the value of configuration settings such as GENERATE_TREEVIEW!
 # 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 
 # 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 
 # 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
 # Visual Studio 2010
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gameplay", "gameplay\gameplay.vcxproj", "{1032BA4B-57EB-4348-9E03-29DD63E80E4A}"
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gameplay", "gameplay\gameplay.vcxproj", "{1032BA4B-57EB-4348-9E03-29DD63E80E4A}"
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	EndProjectSection
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	EndProjectSection
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	EndProjectSection
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	EndProjectSection
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	EndProjectSection
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	EndProjectSection
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	EndProjectSection
 EndProject
 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
 	ProjectSection(ProjectDependencies) = postProject
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 	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">
       location = "group:gameplay/gameplay.xcodeproj">
    </FileRef>
    </FileRef>
    <FileRef
    <FileRef
-      location = "group:gameplay-tests/gameplay-tests.xcodeproj">
+      location = "group:samples/browser/sample-browser.xcodeproj">
    </FileRef>
    </FileRef>
    <FileRef
    <FileRef
-      location = "group:gameplay-samples/sample00-mesh/sample00-mesh.xcodeproj">
+      location = "group:samples/character/sample-character.xcodeproj">
    </FileRef>
    </FileRef>
    <FileRef
    <FileRef
-      location = "group:gameplay-samples/sample01-longboard/sample01-longboard.xcodeproj">
+      location = "group:samples/longboard/sample-longboard.xcodeproj">
    </FileRef>
    </FileRef>
    <FileRef
    <FileRef
-      location = "group:gameplay-samples/sample02-spaceship/sample02-spaceship.xcodeproj">
-   </FileRef>
+      location = "group:samples/lua/sample-lua.xcodeproj">
+   </FileRef>  
    <FileRef
    <FileRef
-      location = "group:gameplay-samples/sample03-character/sample03-character.xcodeproj">
+      location = "group:samples/mesh/sample-mesh.xcodeproj">
    </FileRef>
    </FileRef>
    <FileRef
    <FileRef
-      location = "group:gameplay-samples/sample04-particles/sample04-particles.xcodeproj">
+      location = "group:samples/particles/sample-particles.xcodeproj">
    </FileRef>
    </FileRef>
    <FileRef
    <FileRef
-      location = "group:gameplay-samples/sample05-lua/sample05-lua.xcodeproj">
+      location = "group:samples/racer/sample-racer.xcodeproj">
    </FileRef>
    </FileRef>
    <FileRef
    <FileRef
-      location = "group:gameplay-samples/sample06-racer/sample06-racer.xcodeproj">
+      location = "group:samples/spaceship/sample-spaceship.xcodeproj">
    </FileRef>
    </FileRef>
 </Workspace>
 </Workspace>

+ 7 - 0
gameplay/CMakeLists.txt

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

+ 2 - 5
gameplay/android/build.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?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.
     <!-- 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
          It contains the path to the SDK. It should *NOT* be checked into
@@ -40,10 +40,7 @@
     <loadproperties srcFile="project.properties" />
     <loadproperties srcFile="project.properties" />
 
 
     <!-- quick check on sdk.dir -->
     <!-- 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
 <!-- 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
 LOCAL_PATH := $(call my-dir)/../../src
 
 
 include $(CLEAR_VARS)
 include $(CLEAR_VARS)
@@ -54,6 +41,7 @@ LOCAL_SRC_FILES := \
     Gamepad.cpp \
     Gamepad.cpp \
     HeightField.cpp \
     HeightField.cpp \
     Image.cpp \
     Image.cpp \
+	ImageControl.cpp \
     Joint.cpp \
     Joint.cpp \
     Joystick.cpp \
     Joystick.cpp \
     Label.cpp \
     Label.cpp \
@@ -87,6 +75,7 @@ LOCAL_SRC_FILES := \
     PhysicsVehicle.cpp \
     PhysicsVehicle.cpp \
     PhysicsVehicleWheel.cpp \
     PhysicsVehicleWheel.cpp \
     Plane.cpp \
     Plane.cpp \
+    Platform.cpp \
     PlatformAndroid.cpp \
     PlatformAndroid.cpp \
     Properties.cpp \
     Properties.cpp \
     Quaternion.cpp \
     Quaternion.cpp \
@@ -178,6 +167,7 @@ LOCAL_SRC_FILES := \
     lua/lua_Global.cpp \
     lua/lua_Global.cpp \
     lua/lua_HeightField.cpp \
     lua/lua_HeightField.cpp \
     lua/lua_Image.cpp \
     lua/lua_Image.cpp \
+    lua/lua_ImageControl.cpp \
     lua/lua_ImageFormat.cpp \
     lua/lua_ImageFormat.cpp \
     lua/lua_Joint.cpp \
     lua/lua_Joint.cpp \
     lua/lua_Joystick.cpp \
     lua/lua_Joystick.cpp \
@@ -260,6 +250,7 @@ LOCAL_SRC_FILES := \
     lua/lua_Technique.cpp \
     lua/lua_Technique.cpp \
     lua/lua_Terrain.cpp \
     lua/lua_Terrain.cpp \
     lua/lua_TerrainFlags.cpp \
     lua/lua_TerrainFlags.cpp \
+    lua/lua_TerrainListener.cpp \
     lua/lua_TextBox.cpp \
     lua/lua_TextBox.cpp \
     lua/lua_Texture.cpp \
     lua/lua_Texture.cpp \
     lua/lua_TextureFilter.cpp \
     lua/lua_TextureFilter.cpp \

+ 22 - 9
gameplay/gameplay.vcxproj

@@ -91,6 +91,7 @@
     <ClCompile Include="src\gameplay-main-windows.cpp" />
     <ClCompile Include="src\gameplay-main-windows.cpp" />
     <ClCompile Include="src\HeightField.cpp" />
     <ClCompile Include="src\HeightField.cpp" />
     <ClCompile Include="src\Image.cpp" />
     <ClCompile Include="src\Image.cpp" />
+    <ClCompile Include="src\ImageControl.cpp" />
     <ClCompile Include="src\Joint.cpp" />
     <ClCompile Include="src\Joint.cpp" />
     <ClCompile Include="src\Joystick.cpp" />
     <ClCompile Include="src\Joystick.cpp" />
     <ClCompile Include="src\Label.cpp" />
     <ClCompile Include="src\Label.cpp" />
@@ -158,6 +159,7 @@
     <ClCompile Include="src\lua\lua_Global.cpp" />
     <ClCompile Include="src\lua\lua_Global.cpp" />
     <ClCompile Include="src\lua\lua_HeightField.cpp" />
     <ClCompile Include="src\lua\lua_HeightField.cpp" />
     <ClCompile Include="src\lua\lua_Image.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_ImageFormat.cpp" />
     <ClCompile Include="src\lua\lua_Joint.cpp" />
     <ClCompile Include="src\lua\lua_Joint.cpp" />
     <ClCompile Include="src\lua\lua_Joystick.cpp" />
     <ClCompile Include="src\lua\lua_Joystick.cpp" />
@@ -240,6 +242,7 @@
     <ClCompile Include="src\lua\lua_Technique.cpp" />
     <ClCompile Include="src\lua\lua_Technique.cpp" />
     <ClCompile Include="src\lua\lua_Terrain.cpp" />
     <ClCompile Include="src\lua\lua_Terrain.cpp" />
     <ClCompile Include="src\lua\lua_TerrainFlags.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_TextBox.cpp" />
     <ClCompile Include="src\lua\lua_Texture.cpp" />
     <ClCompile Include="src\lua\lua_Texture.cpp" />
     <ClCompile Include="src\lua\lua_TextureFilter.cpp" />
     <ClCompile Include="src\lua\lua_TextureFilter.cpp" />
@@ -292,6 +295,7 @@
     <ClCompile Include="src\PhysicsVehicle.cpp" />
     <ClCompile Include="src\PhysicsVehicle.cpp" />
     <ClCompile Include="src\PhysicsVehicleWheel.cpp" />
     <ClCompile Include="src\PhysicsVehicleWheel.cpp" />
     <ClCompile Include="src\Plane.cpp" />
     <ClCompile Include="src\Plane.cpp" />
+    <ClCompile Include="src\Platform.cpp" />
     <ClCompile Include="src\PlatformAndroid.cpp" />
     <ClCompile Include="src\PlatformAndroid.cpp" />
     <ClCompile Include="src\PlatformBlackBerry.cpp" />
     <ClCompile Include="src\PlatformBlackBerry.cpp" />
     <ClCompile Include="src\PlatformLinux.cpp" />
     <ClCompile Include="src\PlatformLinux.cpp" />
@@ -366,6 +370,7 @@
     <ClInclude Include="src\Gesture.h" />
     <ClInclude Include="src\Gesture.h" />
     <ClInclude Include="src\HeightField.h" />
     <ClInclude Include="src\HeightField.h" />
     <ClInclude Include="src\Image.h" />
     <ClInclude Include="src\Image.h" />
+    <ClInclude Include="src\ImageControl.h" />
     <ClInclude Include="src\Joint.h" />
     <ClInclude Include="src\Joint.h" />
     <ClInclude Include="src\Joystick.h" />
     <ClInclude Include="src\Joystick.h" />
     <ClInclude Include="src\Keyboard.h" />
     <ClInclude Include="src\Keyboard.h" />
@@ -434,6 +439,7 @@
     <ClInclude Include="src\lua\lua_Global.h" />
     <ClInclude Include="src\lua\lua_Global.h" />
     <ClInclude Include="src\lua\lua_HeightField.h" />
     <ClInclude Include="src\lua\lua_HeightField.h" />
     <ClInclude Include="src\lua\lua_Image.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_ImageFormat.h" />
     <ClInclude Include="src\lua\lua_Joint.h" />
     <ClInclude Include="src\lua\lua_Joint.h" />
     <ClInclude Include="src\lua\lua_Joystick.h" />
     <ClInclude Include="src\lua\lua_Joystick.h" />
@@ -516,6 +522,7 @@
     <ClInclude Include="src\lua\lua_Technique.h" />
     <ClInclude Include="src\lua\lua_Technique.h" />
     <ClInclude Include="src\lua\lua_Terrain.h" />
     <ClInclude Include="src\lua\lua_Terrain.h" />
     <ClInclude Include="src\lua\lua_TerrainFlags.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_TextBox.h" />
     <ClInclude Include="src\lua\lua_Texture.h" />
     <ClInclude Include="src\lua\lua_Texture.h" />
     <ClInclude Include="src\lua\lua_TextureFilter.h" />
     <ClInclude Include="src\lua\lua_TextureFilter.h" />
@@ -612,15 +619,21 @@
     <None Include="res\shaders\colored-unlit.vert" />
     <None Include="res\shaders\colored-unlit.vert" />
     <None Include="res\shaders\colored.frag" />
     <None Include="res\shaders\colored.frag" />
     <None Include="res\shaders\colored.vert" />
     <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.frag" />
     <None Include="res\shaders\terrain.vert" />
     <None Include="res\shaders\terrain.vert" />
     <None Include="res\shaders\textured-bumped.frag" />
     <None Include="res\shaders\textured-bumped.frag" />

+ 64 - 28
gameplay/gameplay.vcxproj.filters

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

Різницю між файлами не показано, бо вона завелика
+ 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"?>
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <Scheme
-   LastUpgradeVersion = "0450"
+   LastUpgradeVersion = "0460"
    version = "1.3">
    version = "1.3">
    <BuildAction
    <BuildAction
       parallelizeBuildables = "YES"
       parallelizeBuildables = "YES"

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

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

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

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

+ 12 - 1
gameplay/src/AIStateMachine.cpp

@@ -25,7 +25,18 @@ AIStateMachine::~AIStateMachine()
     {
     {
         (*itr)->release();
         (*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
 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);
     GP_ASSERT(container);
 
 
     // An AbsoluteLayout does nothing to modify the layout of Controls.
     // 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++)
     for (size_t i = 0, count = controls.size(); i < count; i++)
     {
     {
         Control* control = controls[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 begin = pClip->getInt("begin");
         int end = pClip->getInt("end");
         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");
         const char* repeat = pClip->getString("repeatCount");
         if (repeat)
         if (repeat)
@@ -279,6 +279,8 @@ void Animation::createClips(Properties* animationProperties, unsigned int frameC
             clip->setSpeed(value);
             clip->setSpeed(value);
         }
         }
 
 
+        clip->setLoopBlendTime(pClip->getFloat("loopBlendTime")); // returns zero if not specified
+
         pClip = animationProperties->getNextNamespace();
         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)
 AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime)
     : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _duration(_endTime - _startTime), 
     : _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), 
       _elapsedTime(0), _crossFadeToClip(NULL), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), 
       _beginListeners(NULL), _endListeners(NULL), _listeners(NULL), _listenerItr(NULL), _scriptListeners(NULL)
       _beginListeners(NULL), _endListeners(NULL), _listeners(NULL), _listenerItr(NULL), _scriptListeners(NULL)
 {
 {
@@ -106,11 +106,14 @@ void AnimationClip::setRepeatCount(float repeatCount)
 
 
     if (repeatCount == REPEAT_INDEFINITE)
     if (repeatCount == REPEAT_INDEFINITE)
     {
     {
-        _activeDuration = _duration;
+        _activeDuration = _duration + _loopBlendTime;
     }
     }
     else
     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)
 void AnimationClip::setActiveDuration(unsigned long duration)
 {
 {
+    GP_ASSERT(duration > 0.0f);
+
     if (duration == REPEAT_INDEFINITE)
     if (duration == REPEAT_INDEFINITE)
     {
     {
-        _repeatCount = REPEAT_INDEFINITE;
-        _activeDuration = _duration;
+        _activeDuration = _duration + _loopBlendTime;
     }
     }
     else
     else
     {
     {
-        _activeDuration = _duration;
+        _activeDuration = duration;
         _repeatCount = (float)_activeDuration / (float)_duration;
         _repeatCount = (float)_activeDuration / (float)_duration;
     }
     }
 }
 }
@@ -166,6 +170,18 @@ float AnimationClip::getBlendWeight() const
     return _blendWeight;
     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
 bool AnimationClip::isPlaying() const
 {
 {
     return (isClipStateBitSet(CLIP_IS_PLAYING_BIT) && !isClipStateBitSet(CLIP_IS_PAUSED_BIT));
     return (isClipStateBitSet(CLIP_IS_PLAYING_BIT) && !isClipStateBitSet(CLIP_IS_PAUSED_BIT));
@@ -358,57 +374,59 @@ bool AnimationClip::update(float elapsedTime)
     {
     {
         return false;
         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 
         // after the last update call. Reset the flag, and return true so the AnimationClip is removed from the 
         // running clips on the AnimationController.
         // running clips on the AnimationController.
         onEnd();
         onEnd();
         return true;
         return true;
     }
     }
-    else if (!isClipStateBitSet(CLIP_IS_STARTED_BIT))
+
+    if (!isClipStateBitSet(CLIP_IS_STARTED_BIT))
     {
     {
+        // Clip is just starting
         onBegin();
         onBegin();
     }
     }
     else
     else
     {
     {
+        // Clip was already running
         _elapsedTime += elapsedTime * _speed;
         _elapsedTime += elapsedTime * _speed;
 
 
         if (_repeatCount == REPEAT_INDEFINITE && _elapsedTime <= 0)
         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;
             _elapsedTime = _activeDuration + _elapsedTime;
+
+            // TODO: account for _loopBlendTime
+        }
     }
     }
 
 
+    // Current time within a loop of the clip
     float currentTime = 0.0f;
     float currentTime = 0.0f;
+
     // Check to see if clip is complete.
     // Check to see if clip is complete.
     if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0.0f)))
     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);
         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
     else
     {
     {
         // If _duration == 0, we have a "pose". Just set currentTime to 0.
         // If _duration == 0, we have a "pose". Just set currentTime to 0.
         if (_duration == 0)
         if (_duration == 0)
+        {
             currentTime = 0.0f;
             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.
     // 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
     // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
     GP_ASSERT(_animation);
     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))
     if (isClipStateBitSet(CLIP_IS_FADING_OUT_BIT))
     {
     {
         GP_ASSERT(_crossFadeToClip);
         GP_ASSERT(_crossFadeToClip);
@@ -503,6 +524,9 @@ bool AnimationClip::update(float elapsedTime)
     AnimationValue* value = NULL;
     AnimationValue* value = NULL;
     AnimationTarget* target = NULL;
     AnimationTarget* target = NULL;
     size_t channelCount = _animation->_channels.size();
     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++)
     for (size_t i = 0; i < channelCount; i++)
     {
     {
         channel = _animation->_channels[i];
         channel = _animation->_channels[i];
@@ -514,7 +538,8 @@ bool AnimationClip::update(float elapsedTime)
 
 
         // Evaluate the point on Curve
         // Evaluate the point on Curve
         GP_ASSERT(channel->getCurve());
         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.
         // Set the animation value on the target property.
         target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
         target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
     }
     }
@@ -531,6 +556,8 @@ bool AnimationClip::update(float elapsedTime)
 
 
 void AnimationClip::onBegin()
 void AnimationClip::onBegin()
 {
 {
+    addRef();
+
     // Initialize animation to play.
     // Initialize animation to play.
     setClipStateBit(CLIP_IS_STARTED_BIT);
     setClipStateBit(CLIP_IS_STARTED_BIT);
     if (_speed >= 0)
     if (_speed >= 0)
@@ -559,10 +586,14 @@ void AnimationClip::onBegin()
             listener++;
             listener++;
         }
         }
     }
     }
+
+    release();
 }
 }
 
 
 void AnimationClip::onEnd()
 void AnimationClip::onEnd()
 {
 {
+    addRef();
+
     _blendWeight = 1.0f;
     _blendWeight = 1.0f;
     resetClipStateBit(CLIP_ALL_BITS);
     resetClipStateBit(CLIP_ALL_BITS);
 
 
@@ -577,6 +608,8 @@ void AnimationClip::onEnd()
             listener++;
             listener++;
         }
         }
     }
     }
+
+    release();
 }
 }
 
 
 bool AnimationClip::isClipStateBitSet(unsigned char bit) const
 bool AnimationClip::isClipStateBitSet(unsigned char bit) const

+ 17 - 0
gameplay/src/AnimationClip.h

@@ -178,6 +178,22 @@ public:
      */
      */
     float getBlendWeight() const;
     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.
      * Checks if the AnimationClip is playing.
      *
      *
@@ -392,6 +408,7 @@ private:
     unsigned long _duration;                            // The total duration.
     unsigned long _duration;                            // The total duration.
     unsigned char _stateBits;                           // Bit flag used to keep track of the clip's current state.
     unsigned char _stateBits;                           // Bit flag used to keep track of the clip's current state.
     float _repeatCount;                                 // The clip's repeat count.
     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.
     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.
     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.
     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);
         AnimationClip* clip = (*clipIter);
         GP_ASSERT(clip);
         GP_ASSERT(clip);
+        clip->addRef();
         if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
         if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
         {   // If the CLIP_IS_RESTARTED_BIT is set, we should end the clip and 
         {   // 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.
             // 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))
         else if (clip->update(elapsedTime))
         {
         {
-            SAFE_RELEASE(clip);
+            clip->release();
             clipIter = _runningClips.erase(clipIter);
             clipIter = _runningClips.erase(clipIter);
         }
         }
         else
         else
         {
         {
             clipIter++;
             clipIter++;
         }
         }
+        clip->release();
     }
     }
 
 
     Transform::resumeTransformChanged();
     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)
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
             {
                 _contactIndex = (int) contactIndex;
                 _contactIndex = (int) contactIndex;
-
                 setState(Control::ACTIVE);
                 setState(Control::ACTIVE);
-
-                notifyListeners(Listener::PRESS);
-
+                notifyListeners(Control::Listener::PRESS);
                 return _consumeInputEvents;
                 return _consumeInputEvents;
             }
             }
             else
             else
@@ -68,14 +65,13 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         if (_contactIndex == (int) contactIndex)
         if (_contactIndex == (int) contactIndex)
         {
         {
             _contactIndex = INVALID_CONTACT_INDEX;
             _contactIndex = INVALID_CONTACT_INDEX;
-            notifyListeners(Listener::RELEASE);
+            notifyListeners(Control::Listener::RELEASE);
             if (!_parent->isScrolling() &&
             if (!_parent->isScrolling() &&
                 x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
             {
                 setState(Control::FOCUS);
                 setState(Control::FOCUS);
-
-                notifyListeners(Listener::CLICK);
+                notifyListeners(Control::Listener::CLICK);
             }
             }
             else
             else
             {
             {
@@ -93,6 +89,40 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return false;
     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
 const char* Button::getType() const
 {
 {
     return "button";
     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);
     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
      * @see Control::getType
      */
      */

+ 41 - 22
gameplay/src/Camera.cpp

@@ -14,18 +14,21 @@
 #define CAMERA_DIRTY_BOUNDS 32
 #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)
 #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
 namespace gameplay
 {
 {
 
 
 Camera::Camera(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
 Camera::Camera(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
     : _type(PERSPECTIVE), _fieldOfView(fieldOfView), _aspectRatio(aspectRatio), _nearPlane(nearPlane), _farPlane(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)
 Camera::Camera(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane)
     : _type(ORTHOGRAPHIC), _aspectRatio(aspectRatio), _nearPlane(nearPlane), _farPlane(farPlane),
     : _type(ORTHOGRAPHIC), _aspectRatio(aspectRatio), _nearPlane(nearPlane), _farPlane(farPlane),
-      _dirtyBits(CAMERA_DIRTY_ALL), _node(NULL)
+      _bits(CAMERA_DIRTY_ALL), _node(NULL)
 {
 {
     // Orthographic camera.
     // Orthographic camera.
     _zoom[0] = zoomX;
     _zoom[0] = zoomX;
@@ -131,7 +134,7 @@ void Camera::setFieldOfView(float fieldOfView)
     GP_ASSERT(_type == Camera::PERSPECTIVE);
     GP_ASSERT(_type == Camera::PERSPECTIVE);
 
 
     _fieldOfView = fieldOfView;
     _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
 float Camera::getZoomX() const
@@ -146,7 +149,7 @@ void Camera::setZoomX(float zoomX)
     GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
     GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
 
     _zoom[0] = zoomX;
     _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
 float Camera::getZoomY() const
@@ -161,7 +164,7 @@ void Camera::setZoomY(float zoomY)
     GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
     GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
 
     _zoom[1] = zoomY;
     _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
 float Camera::getAspectRatio() const
@@ -172,7 +175,7 @@ float Camera::getAspectRatio() const
 void Camera::setAspectRatio(float aspectRatio)
 void Camera::setAspectRatio(float aspectRatio)
 {
 {
     _aspectRatio = 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
 float Camera::getNearPlane() const
@@ -183,7 +186,7 @@ float Camera::getNearPlane() const
 void Camera::setNearPlane(float nearPlane)
 void Camera::setNearPlane(float nearPlane)
 {
 {
     _nearPlane = 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
 float Camera::getFarPlane() const
@@ -194,7 +197,7 @@ float Camera::getFarPlane() const
 void Camera::setFarPlane(float farPlane)
 void Camera::setFarPlane(float farPlane)
 {
 {
     _farPlane = 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
 Node* Camera::getNode() const
@@ -219,13 +222,13 @@ void Camera::setNode(Node* node)
             _node->addListener(this);
             _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
 const Matrix& Camera::getViewMatrix() const
 {
 {
-    if (_dirtyBits & CAMERA_DIRTY_VIEW)
+    if (_bits & CAMERA_DIRTY_VIEW)
     {
     {
         if (_node)
         if (_node)
         {
         {
@@ -237,7 +240,7 @@ const Matrix& Camera::getViewMatrix() const
             _view.setIdentity();
             _view.setIdentity();
         }
         }
 
 
-        _dirtyBits &= ~CAMERA_DIRTY_VIEW;
+        _bits &= ~CAMERA_DIRTY_VIEW;
     }
     }
 
 
     return _view;
     return _view;
@@ -245,11 +248,11 @@ const Matrix& Camera::getViewMatrix() const
 
 
 const Matrix& Camera::getInverseViewMatrix() const
 const Matrix& Camera::getInverseViewMatrix() const
 {
 {
-    if (_dirtyBits & CAMERA_DIRTY_INV_VIEW)
+    if (_bits & CAMERA_DIRTY_INV_VIEW)
     {
     {
         getViewMatrix().invert(&_inverseView);
         getViewMatrix().invert(&_inverseView);
 
 
-        _dirtyBits &= ~CAMERA_DIRTY_INV_VIEW;
+        _bits &= ~CAMERA_DIRTY_INV_VIEW;
     }
     }
 
 
     return _inverseView;
     return _inverseView;
@@ -257,7 +260,7 @@ const Matrix& Camera::getInverseViewMatrix() const
 
 
 const Matrix& Camera::getProjectionMatrix() const
 const Matrix& Camera::getProjectionMatrix() const
 {
 {
-    if (_dirtyBits & CAMERA_DIRTY_PROJ)
+    if (!(_bits & CAMERA_CUSTOM_PROJECTION) && (_bits & CAMERA_DIRTY_PROJ))
     {
     {
         if (_type == PERSPECTIVE)
         if (_type == PERSPECTIVE)
         {
         {
@@ -268,19 +271,35 @@ const Matrix& Camera::getProjectionMatrix() const
             Matrix::createOrthographic(_zoom[0], _zoom[1], _nearPlane, _farPlane, &_projection);
             Matrix::createOrthographic(_zoom[0], _zoom[1], _nearPlane, _farPlane, &_projection);
         }
         }
 
 
-        _dirtyBits &= ~CAMERA_DIRTY_PROJ;
+        _bits &= ~CAMERA_DIRTY_PROJ;
     }
     }
 
 
     return _projection;
     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
 const Matrix& Camera::getViewProjectionMatrix() const
 {
 {
-    if (_dirtyBits & CAMERA_DIRTY_VIEW_PROJ)
+    if (_bits & CAMERA_DIRTY_VIEW_PROJ)
     {
     {
         Matrix::multiply(getProjectionMatrix(), getViewMatrix(), &_viewProjection);
         Matrix::multiply(getProjectionMatrix(), getViewMatrix(), &_viewProjection);
 
 
-        _dirtyBits &= ~CAMERA_DIRTY_VIEW_PROJ;
+        _bits &= ~CAMERA_DIRTY_VIEW_PROJ;
     }
     }
 
 
     return _viewProjection;
     return _viewProjection;
@@ -288,11 +307,11 @@ const Matrix& Camera::getViewProjectionMatrix() const
 
 
 const Matrix& Camera::getInverseViewProjectionMatrix() const
 const Matrix& Camera::getInverseViewProjectionMatrix() const
 {
 {
-    if (_dirtyBits & CAMERA_DIRTY_INV_VIEW_PROJ)
+    if (_bits & CAMERA_DIRTY_INV_VIEW_PROJ)
     {
     {
         getViewProjectionMatrix().invert(&_inverseViewProjection);
         getViewProjectionMatrix().invert(&_inverseViewProjection);
 
 
-        _dirtyBits &= ~CAMERA_DIRTY_INV_VIEW_PROJ;
+        _bits &= ~CAMERA_DIRTY_INV_VIEW_PROJ;
     }
     }
 
 
     return _inverseViewProjection;
     return _inverseViewProjection;
@@ -300,12 +319,12 @@ const Matrix& Camera::getInverseViewProjectionMatrix() const
 
 
 const Frustum& Camera::getFrustum() 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.
         // Update our bounding frustum from our view projection matrix.
         _bounds.set(getViewProjectionMatrix());
         _bounds.set(getViewProjectionMatrix());
 
 
-        _dirtyBits &= ~CAMERA_DIRTY_BOUNDS;
+        _bits &= ~CAMERA_DIRTY_BOUNDS;
     }
     }
 
 
     return _bounds;
     return _bounds;
@@ -404,7 +423,7 @@ Camera* Camera::clone(NodeCloneContext &context) const
 
 
 void Camera::transformChanged(Transform* transform, long cookie)
 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;
     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.
      * Gets the camera's view * projection matrix.
      *
      *
@@ -299,7 +319,7 @@ private:
     mutable Matrix _inverseView;
     mutable Matrix _inverseView;
     mutable Matrix _inverseViewProjection;
     mutable Matrix _inverseViewProjection;
     mutable Frustum _bounds;
     mutable Frustum _bounds;
-    mutable int _dirtyBits;
+    mutable int _bits;
     Node* _node;
     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);
     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)
 void CheckBox::update(const Control* container, const Vector2& offset)
 {
 {
     Label::update(container, 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);
     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
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.

+ 562 - 51
gameplay/src/Container.cpp

@@ -11,6 +11,7 @@
 #include "Slider.h"
 #include "Slider.h"
 #include "TextBox.h"
 #include "TextBox.h"
 #include "Joystick.h"
 #include "Joystick.h"
+#include "ImageControl.h"
 #include "Game.h"
 #include "Game.h"
 
 
 namespace gameplay
 namespace gameplay
@@ -22,6 +23,12 @@ static const long SCROLL_INERTIA_DELAY = 100L;
 static const float SCROLL_FRICTION_FACTOR = 5.0f;
 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.
 // 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;
 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.
  * Sort function for use with _controls.sort(), based on Z-Order.
@@ -42,7 +49,11 @@ Container::Container()
       _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
       _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
       _scrollingRight(false), _scrollingDown(false),
       _scrollingRight(false), _scrollingDown(false),
       _scrollingMouseVertically(false), _scrollingMouseHorizontally(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)
       _contactIndices(0), _initializedWithScroll(false)
 {
 {
 }
 }
@@ -165,6 +176,10 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
         {
             control = Joystick::create(controlStyle, controlSpace);
             control = Joystick::create(controlStyle, controlSpace);
         }
         }
+        else if (controlName == "IMAGE")
+        {
+            control = ImageControl::create(controlStyle, controlSpace);
+        }
         else
         else
         {
         {
             // Ignore - not a valid control name.
             // Ignore - not a valid control name.
@@ -177,20 +192,6 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
         {
             addControl(control);
             addControl(control);
             control->release();
             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.
         // Get the next control.
@@ -198,7 +199,7 @@ void Container::addControls(Theme* theme, Properties* properties)
     }
     }
 
 
     // Sort controls by Z-Order.
     // Sort controls by Z-Order.
-    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
+    sortControls();
 }
 }
 
 
 Layout* Container::getLayout()
 Layout* Container::getLayout()
@@ -215,11 +216,26 @@ unsigned int Container::addControl(Control* control)
         control->_parent->removeControl(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)
     if (control->_parent != this)
     {
     {
         _controls.push_back(control);
         _controls.push_back(control);
         control->addRef();
         control->addRef();
         control->_parent = this;
         control->_parent = this;
+        sortControls();
         return (unsigned int)(_controls.size() - 1);
         return (unsigned int)(_controls.size() - 1);
     }
     }
     else
     else
@@ -433,9 +449,6 @@ void Container::update(const Control* container, const Vector2& offset)
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
     }
 
 
-    // Sort controls by Z-Order.
-    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
-
     GP_ASSERT(_layout);
     GP_ASSERT(_layout);
     if (_scroll != SCROLL_NONE)
     if (_scroll != SCROLL_NONE)
     {
     {
@@ -615,40 +628,524 @@ bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
             continue;
             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;
                 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();
                         release();
                         return _consumeInputEvents;
                         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();
     release();
-    return false;
+    return eventConsumed;
 }
 }
 
 
 bool Container::isContainer() const
 bool Container::isContainer() const
@@ -692,11 +1189,14 @@ void Container::updateScroll()
         _layout->update(this, _scrollPosition);
         _layout->update(this, _scrollPosition);
     }
     }
 
 
-    // Update Time.
-    static double lastFrameTime = Game::getGameTime();
+    // Update time.
+    if (!_lastFrameTime)
+    {
+        _lastFrameTime = Game::getGameTime();
+    }
     double frameTime = 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::Border& containerBorder = getBorder(_state);
     const Theme::Padding& containerPadding = getPadding();
     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;
     float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
 
 
     // Apply and dampen inertia.
     // Apply and dampen inertia.
-    if (!_scrolling && !_scrollingVelocity.isZero())
+    if (!_scrollingVelocity.isZero())
     {
     {
         // Calculate the time passed since last update.
         // Calculate the time passed since last update.
         float elapsedSecs = (float)elapsedTime * 0.001f;
         float elapsedSecs = (float)elapsedTime * 0.001f;
@@ -737,14 +1237,17 @@ void Container::updateScroll()
         _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
         _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
         _scrollPosition.y += _scrollingVelocity.y * 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.
     // Stop scrolling when the far edge is reached.
@@ -791,7 +1294,7 @@ void Container::updateScroll()
         _scrollBarOpacity = 0.99f;
         _scrollBarOpacity = 0.99f;
         if (!_scrollBarOpacityClip)
         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 = animation->getClip();
         }
         }
         _scrollBarOpacityClip->play();
         _scrollBarOpacityClip->play();
@@ -801,6 +1304,14 @@ void Container::updateScroll()
     _layout->update(this, _scrollPosition);
     _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)
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
 {
     switch(evt)
     switch(evt)
@@ -1180,7 +1691,7 @@ unsigned int Container::getAnimationPropertyComponentCount(int propertyId) const
 {
 {
     switch(propertyId)
     switch(propertyId)
     {
     {
-    case ANIMATE_OPACITY:
+    case ANIMATE_SCROLLBAR_OPACITY:
         return 1;
         return 1;
     default:
     default:
         return Control::getAnimationPropertyComponentCount(propertyId);
         return Control::getAnimationPropertyComponentCount(propertyId);
@@ -1193,7 +1704,7 @@ void Container::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 
 
     switch(propertyId)
     switch(propertyId)
     {
     {
-    case ANIMATE_OPACITY:
+    case ANIMATE_SCROLLBAR_OPACITY:
         value->setFloat(0, _scrollBarOpacity);
         value->setFloat(0, _scrollBarOpacity);
         break;
         break;
     default:
     default:
@@ -1208,7 +1719,7 @@ void Container::setAnimationPropertyValue(int propertyId, AnimationValue* value,
 
 
     switch(propertyId)
     switch(propertyId)
     {
     {
-    case ANIMATE_OPACITY:
+    case ANIMATE_SCROLLBAR_OPACITY:
         _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
         _scrollBarOpacity = Curve::lerp(blendWeight, _opacity, value->getFloat(0));
         _dirty = true;
         _dirty = true;
         break;
         break;

+ 54 - 4
gameplay/src/Container.h

@@ -3,6 +3,7 @@
 
 
 #include "Control.h"
 #include "Control.h"
 #include "Layout.h"
 #include "Layout.h"
+#include "TimeListener.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -44,7 +45,7 @@ namespace gameplay
     }
     }
  @endverbatim
  @endverbatim
  */
  */
-class Container : public Control
+class Container : public Control, TimeListener
 {
 {
 
 
 public:
 public:
@@ -212,6 +213,13 @@ public:
      */
      */
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 
+    /**
+     * @see TimeListener::timeEvent
+     *
+     * @script{ignore}
+     */
+    void timeEvent(long timeDiff, void* cookie);
+
 protected:
 protected:
 
 
     /**
     /**
@@ -291,10 +299,17 @@ protected:
      *
      *
      * @return True if the mouse event is consumed or false if it is not consumed.
      * @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);
     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.
      * Gets a Layout::Type enum from a matching string.
      *
      *
@@ -334,6 +349,13 @@ protected:
      */
      */
     void updateScroll();
     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.
      * Applies touch events to scroll state.
      *
      *
@@ -513,16 +535,44 @@ private:
      */
      */
     Container(const Container& copy);
     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;
     AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
     int _zIndexDefault;
     int _focusIndexDefault;
     int _focusIndexDefault;
     int _focusIndexMax;
     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 _totalWidth;
     float _totalHeight;
     float _totalHeight;
-
     int _contactIndices;
     int _contactIndices;
-
     bool _initializedWithScroll;
     bool _initializedWithScroll;
 };
 };
 
 

+ 77 - 35
gameplay/src/Control.cpp

@@ -8,7 +8,7 @@ namespace gameplay
 Control::Control()
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
     : _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),
     _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]");
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
 }
 }
@@ -17,9 +17,9 @@ Control::~Control()
 {
 {
     if (_listeners)
     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(list);
         }
         }
         SAFE_DELETE(_listeners);
         SAFE_DELETE(_listeners);
@@ -187,6 +187,11 @@ const Rectangle& Control::getBounds() const
     return _bounds;
     return _bounds;
 }
 }
 
 
+const Rectangle& Control::getAbsoluteBounds() const
+{
+    return _absoluteBounds;
+}
+
 float Control::getX() const
 float Control::getX() const
 {
 {
     return _bounds.x;
     return _bounds.x;
@@ -691,29 +696,29 @@ void Control::addListener(Control::Listener* listener, int eventFlags)
 {
 {
     GP_ASSERT(listener);
     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)
     if (_listeners == NULL || listener == NULL)
         return;
         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);
         itr->second->remove(listener);
 
 
         if(itr->second->empty())
         if(itr->second->empty())
         {
         {
-            std::list<Listener*>* list = itr->second;
+            std::list<Control::Listener*>* list = itr->second;
             _listeners->erase(itr++);
             _listeners->erase(itr++);
             SAFE_DELETE(list);
             SAFE_DELETE(list);
         }
         }
@@ -740,23 +745,23 @@ void Control::removeListener(Control::Listener* listener)
         SAFE_DELETE(_listeners);
         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);
     GP_ASSERT(listener);
 
 
     if (!_listeners)
     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())
     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);
         itr = _listeners->find(eventType);
     }
     }
 
 
-    std::list<Listener*>* listenerList = itr->second;
+    std::list<Control::Listener*>* listenerList = itr->second;
     listenerList->push_back(listener);
     listenerList->push_back(listener);
 }
 }
 
 
@@ -773,7 +778,7 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
         {
             _contactIndex = (int) contactIndex;
             _contactIndex = (int) contactIndex;
 
 
-            notifyListeners(Listener::PRESS);
+            notifyListeners(Control::Listener::PRESS);
 
 
             return _consumeInputEvents;
             return _consumeInputEvents;
         }
         }
@@ -789,15 +794,15 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
         {
             _contactIndex = INVALID_CONTACT_INDEX;
             _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 &&
             if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
             {
                 // Leave this control in the FOCUS state.
                 // Leave this control in the FOCUS state.
-                notifyListeners(Listener::CLICK);
+                notifyListeners(Control::Listener::CLICK);
             }
             }
 
 
             return _consumeInputEvents;
             return _consumeInputEvents;
@@ -834,7 +839,42 @@ bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
     return false;
     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.
     // This method runs untrusted code by notifying listeners of events.
     // If the user calls exit() or otherwise releases this control, we
     // If the user calls exit() or otherwise releases this control, we
@@ -843,11 +883,11 @@ void Control::notifyListeners(Listener::EventType eventType)
 
 
     if (_listeners)
     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())
         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);
                 GP_ASSERT(*listenerItr);
                 (*listenerItr)->controlEvent(this, eventType);
                 (*listenerItr)->controlEvent(this, eventType);
@@ -951,9 +991,9 @@ void Control::update(const Control* container, const Vector2& offset)
  
  
     _viewportClipBounds.set(x, y, width, height);
     _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())
     if (_clearBounds.isEmpty())
     {
     {
         _clearBounds.set(_absoluteClipBounds);
         _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);
             spriteBatch->draw(rightX, _absoluteBounds.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
         if (border.left)
         if (border.left)
             spriteBatch->draw(_absoluteBounds.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
             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)
         if (border.right)
             spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
             spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
         if (border.bottom && border.left)
         if (border.bottom && border.left)

+ 37 - 17
gameplay/src/Control.h

@@ -11,6 +11,7 @@
 #include "Keyboard.h"
 #include "Keyboard.h"
 #include "Mouse.h"
 #include "Mouse.h"
 #include "ScriptTarget.h"
 #include "ScriptTarget.h"
+#include "Gamepad.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -85,12 +86,6 @@ public:
         ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT
         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()
      * Implement Control::Listener and call Control::addListener()
      * in order to listen for events on controls.
      * in order to listen for events on controls.
@@ -156,6 +151,12 @@ public:
         virtual void controlEvent(Control* control, EventType evt) = 0;
         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
      * Position animation property. Data = x, y
      */
      */
@@ -244,6 +245,14 @@ public:
      */
      */
     const Rectangle& getBounds() const;
     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.
      * 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);
     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
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
@@ -850,6 +868,14 @@ protected:
      */
      */
     virtual void update(const Control* container, const Vector2& offset);
     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.
      * Draw the images associated with this control.
      *
      *
@@ -915,7 +941,8 @@ protected:
      *
      *
      * @param eventType The event to trigger.
      * @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.
      * Gets the Alignment by string.
@@ -1003,7 +1030,8 @@ protected:
     /**
     /**
      * Listeners map of EventType's to a list of Listeners.
      * 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.
      * The Control's Theme::Style.
@@ -1063,15 +1091,7 @@ private:
 
 
     Theme::Skin* getSkin(State state);
     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;
     bool _styleOverridden;
     Theme::Skin* _skin;
     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
 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);
         memcpy(dst, _points[0].value, _componentSize);
         return;
         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;
         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.
     // Calculate the value of the curve discretely if appropriate.
     switch (from->type)
     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);
         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 a binary search to determine the index.
     do 
     do 

+ 34 - 2
gameplay/src/Curve.h

@@ -345,13 +345,45 @@ public:
     void setTangent(unsigned int index, InterpolationType type, float* inValue, float* outValue);
     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 time The position to evaluate the curve at.
      * @param dst The evaluated value of the curve at the given time.
      * @param dst The evaluated value of the curve at the given time.
      */
      */
     void evaluate(float time, float* dst) const;
     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.
      * Linear interpolation function.
      */
      */
@@ -459,7 +491,7 @@ private:
     /**
     /**
      * Determines the current keyframe to interpolate from based on the specified time.
      * 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
      * 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::string __resourcePath("./");
 static std::map<std::string, std::string> __aliases;
 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}
  * @script{ignore}
@@ -259,30 +280,54 @@ bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
         path.append(dirPath);
         path.append(dirPath);
     }
     }
     path.append("/.");
     path.append("/.");
+    bool result = false;
+
     struct dirent* dp;
     struct dirent* dp;
     DIR* dir = opendir(path.c_str());
     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
 #endif
 }
 }
 
 
@@ -297,13 +342,13 @@ bool FileSystem::fileExists(const char* filePath)
     }
     }
 #endif
 #endif
 
 
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(filePath);
+    std::string fullPath;
+    getFullPath(filePath, fullPath);
 
 
     gp_stat_struct s;
     gp_stat_struct s;
 
 
 #ifdef WIN32
 #ifdef WIN32
-    if (stat(fullPath.c_str(), &s) != 0)
+    if (!isAbsolutePath(filePath) && stat(fullPath.c_str(), &s) != 0)
     {
     {
         fullPath = __resourcePath;
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
         fullPath += "../../gameplay/";
@@ -352,12 +397,12 @@ Stream* FileSystem::open(const char* path, size_t mode)
         return FileStreamAndroid::create(resolvePath(path), modeStr);
         return FileStreamAndroid::create(resolvePath(path), modeStr);
     }
     }
 #else
 #else
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(path);
+    std::string fullPath;
+    getFullPath(path, fullPath);
     
     
 #ifdef WIN32
 #ifdef WIN32
     gp_stat_struct s;
     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 = __resourcePath;
         fullPath += "../../gameplay/";
         fullPath += "../../gameplay/";
@@ -386,15 +431,15 @@ FILE* FileSystem::openFile(const char* filePath, const char* mode)
     GP_ASSERT(filePath);
     GP_ASSERT(filePath);
     GP_ASSERT(mode);
     GP_ASSERT(mode);
 
 
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(filePath);
+    std::string fullPath;
+    getFullPath(filePath, fullPath);
 
 
     createFileFromAsset(filePath);
     createFileFromAsset(filePath);
     
     
     FILE* fp = fopen(fullPath.c_str(), mode);
     FILE* fp = fopen(fullPath.c_str(), mode);
     
     
 #ifdef WIN32
 #ifdef WIN32
-    if (fp == NULL)
+    if (fp == NULL && !isAbsolutePath(filePath))
     {
     {
         fullPath = __resourcePath;
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
         fullPath += "../../gameplay/";
@@ -452,11 +497,10 @@ bool FileSystem::isAbsolutePath(const char* filePath)
     if (filePath == 0 || filePath[0] == '\0')
     if (filePath == 0 || filePath[0] == '\0')
         return false;
         return false;
 #ifdef WIN32
 #ifdef WIN32
-    if (strlen(filePath) >= 2)
+    if (filePath[1] != '\0')
     {
     {
         char first = filePath[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;
     return false;
 #else
 #else

+ 163 - 127
gameplay/src/Form.cpp

@@ -389,138 +389,138 @@ void Form::setNode(Node* node)
 }
 }
 
 
 void Form::update(float elapsedTime)
 void Form::update(float elapsedTime)
-{
-    updateBounds();
-}
-
-void Form::updateBounds()
 {
 {
     if (isDirty())
     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
         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;
         x = 0;
+    }
+
+    if (y < 0)
+    {
+        height += y;
+        y = -y;
+    }
+    else
+    {
         y = 0;
         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);
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
         _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),
         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);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
 
-        // restore the previous game viewport
+        // Restore the previous game viewport.
         game->setViewport(prevViewport);
         game->setViewport(prevViewport);
         // Rebind the previous framebuffer and game viewport.
         // Rebind the previous framebuffer and game viewport.
         previousFrameBuffer->bind();
         previousFrameBuffer->bind();
     }
     }
 
 
-    // Draw either with a 3D quad or sprite batch
+    // Draw either with a 3D quad or sprite batch.
     if (_node)
     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();
         _nodeQuad->draw();
     }
     }
     else
     else
@@ -581,11 +586,25 @@ const char* Form::getType() const
     return "form";
     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)
 bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
 {
     // Check for a collision with each Form in __forms.
     // Check for a collision with each Form in __forms.
     // Pass the event on.
     // Pass the event on.
-    bool eventConsumed = false;
     size_t size = __forms.size();
     size_t size = __forms.size();
     for (size_t i = 0; i < size; ++i)
     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 &&
                          point.y <= bounds.y + bounds.height))
                          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))
                         y <= bounds.y + bounds.height))
                 {
                 {
                     // Pass on the event's position relative to the form.
                     // 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)
 bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
@@ -638,7 +659,7 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     {
     {
         Form* form = __forms[i];
         Form* form = __forms[i];
         GP_ASSERT(form);
         GP_ASSERT(form);
-        if (form->isEnabled() && form->isVisible())
+        if (form->isEnabled() && form->isVisible() && form->getState() == Control::FOCUS)
         {
         {
             if (form->keyEvent(evt, key))
             if (form->keyEvent(evt, key))
                 return true;
                 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 Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 {
 {
-    bool eventConsumed = false;
-
     for (size_t i = 0; i < __forms.size(); ++i)
     for (size_t i = 0; i < __forms.size(); ++i)
     {
     {
         Form* form = __forms[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 &&
                          point.y <= bounds.y + bounds.height))
                          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))
                         y <= bounds.y + bounds.height))
                 {
                 {
                     // Pass on the event's position relative to the form.
                     // 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)
 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.
         // Get info about the form's position.
         Matrix m = _node->getWorldMatrix();
         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.
         // Unproject point into world space.
         Ray ray;
         Ray ray;
         camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
         camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
 
 
         // Find the quad's plane.  We know its normal is the quad's forward vector.
         // 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);
         Plane plane(normal, -distance);
 
 
         // Check for collision with plane.
         // Check for collision with plane.

+ 17 - 0
gameplay/src/Form.h

@@ -9,6 +9,7 @@
 #include "Touch.h"
 #include "Touch.h"
 #include "Keyboard.h"
 #include "Keyboard.h"
 #include "Mouse.h"
 #include "Mouse.h"
+#include "Gamepad.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -48,6 +49,8 @@ class Theme;
 class Form : public Container
 class Form : public Container
 {
 {
     friend class Platform;
     friend class Platform;
+    friend class Game;
+    friend class Gamepad;
 
 
 public:
 public:
 
 
@@ -188,6 +191,11 @@ private:
      */
      */
     void updateBounds();
     void updateBounds();
 
 
+    /**
+     * Updates all visible, enabled forms.
+     */
+    static void updateInternal(float elapsedTime);
+
     /**
     /**
      * Propagate touch events to enabled forms.
      * Propagate touch events to enabled forms.
      *
      *
@@ -211,6 +219,15 @@ private:
      */
      */
     static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
     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.
      * 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) :
 FrameBuffer::FrameBuffer(const char* id, unsigned int width, unsigned int height, FrameBufferHandle handle) :
     _id(id ? id : ""), _width(width), _height(height), _handle(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;
         return;
 
 
     // Release our reference to the current RenderTarget at this index.
     // 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;
     _renderTargets[index] = target;
 
 
@@ -170,6 +174,8 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
     {
     {
         GP_ASSERT( _renderTargets[index]->getTexture() );
         GP_ASSERT( _renderTargets[index]->getTexture() );
 
 
+        ++_renderTargetCount;
+
         // This FrameBuffer now references the RenderTarget.
         // This FrameBuffer now references the RenderTarget.
         target->addRef();
         target->addRef();
 
 
@@ -199,6 +205,11 @@ RenderTarget* FrameBuffer::getRenderTarget(unsigned int index) const
     return NULL;
     return NULL;
 }
 }
 
 
+unsigned int FrameBuffer::getRenderTargetCount() const
+{
+    return _renderTargetCount;
+}
+
 void FrameBuffer::setDepthStencilTarget(DepthStencilTarget* target)
 void FrameBuffer::setDepthStencilTarget(DepthStencilTarget* target)
 {
 {
     if (_depthStencilTarget == target)
     if (_depthStencilTarget == target)
@@ -245,6 +256,11 @@ DepthStencilTarget* FrameBuffer::getDepthStencilTarget() const
     return _depthStencilTarget;
     return _depthStencilTarget;
 }
 }
 
 
+bool FrameBuffer::isDefault() const
+{
+    return (this == _defaultFrameBuffer);
+}
+
 FrameBuffer* FrameBuffer::bind()
 FrameBuffer* FrameBuffer::bind()
 {
 {
     GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
     GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
@@ -260,4 +276,9 @@ FrameBuffer* FrameBuffer::bindDefault()
     return _defaultFrameBuffer;
     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.
      * @return The RenderTarget attached at the specified index.
      */
      */
     RenderTarget* getRenderTarget(unsigned int index = 0) const;
     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.
      * Set this FrameBuffer's DepthStencilTarget.
@@ -128,6 +135,13 @@ public:
      */
      */
     DepthStencilTarget* getDepthStencilTarget() const;
     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.
      * Binds this FrameBuffer for off-screen rendering and return you the curently bound one.
      *
      *
@@ -143,6 +157,13 @@ public:
      * @ return The default framebuffer.
      * @ return The default framebuffer.
      */
      */
     static FrameBuffer* bindDefault(); 
     static FrameBuffer* bindDefault(); 
+
+    /**
+     * Gets the currently bound FrameBuffer.
+     *
+     * @return The currently bound FrameBuffer.
+     */
+    static FrameBuffer* getCurrent();
      
      
 private:
 private:
 
 
@@ -172,6 +193,7 @@ private:
     unsigned int _height;
     unsigned int _height;
     FrameBufferHandle _handle;
     FrameBufferHandle _handle;
     RenderTarget** _renderTargets;
     RenderTarget** _renderTargets;
+    unsigned int _renderTargetCount;
     DepthStencilTarget* _depthStencilTarget;
     DepthStencilTarget* _depthStencilTarget;
 
 
     static unsigned int _maxRenderTargets;
     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
 void Frustum::getCorners(Vector3* corners) const
+{
+    getNearCorners(corners);
+    getFarCorners(corners + 4);
+}
+
+void Frustum::getNearCorners(Vector3* corners) const
 {
 {
     GP_ASSERT(corners);
     GP_ASSERT(corners);
 
 
@@ -69,10 +75,16 @@ void Frustum::getCorners(Vector3* corners) const
     Plane::intersection(_near, _left, _bottom, &corners[1]);
     Plane::intersection(_near, _left, _bottom, &corners[1]);
     Plane::intersection(_near, _right, _bottom, &corners[2]);
     Plane::intersection(_near, _right, _bottom, &corners[2]);
     Plane::intersection(_near, _right, _top, &corners[3]);
     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
 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)
      * (N-near, F-far, L-left, R-right, B-bottom, T-top)
      * LTN, LBN, RBN, RTN, RTF, RBF, LBF, LTF.
      * 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;
     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.
      * Tests whether this frustum intersects the specified point.
      *
      *

+ 56 - 21
gameplay/src/Game.cpp

@@ -86,6 +86,7 @@ int Game::run()
         shutdown();
         shutdown();
         return -2;
         return -2;
     }
     }
+
     return 0;
     return 0;
 }
 }
 
 
@@ -122,30 +123,22 @@ bool Game::startup()
         Properties* scripts = _properties->getNamespace("scripts", true);
         Properties* scripts = _properties->getNamespace("scripts", true);
         if (scripts)
         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
                 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()
 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
 	// This handles the case of shutting down the script system from
 	// within a script function (which can cause errors).
 	// within a script function (which can cause errors).
 	static ShutdownListener listener;
 	static ShutdownListener listener;
 	schedule(0, &listener);
 	schedule(0, &listener);
+
+#else
+
+    // End the process immediately without a full shutdown
+    ::exit(0);
+
+#endif
 }
 }
 
 
+
 void Game::frame()
 void Game::frame()
 {
 {
     if (!_initialized)
     if (!_initialized)
     {
     {
+        // Perform lazy first time initialization
         initialize();
         initialize();
         _scriptController->initializeGame();
         _scriptController->initializeGame();
         _initialized = true;
         _initialized = true;
+
+        // Fire first game resize event
+        Platform::resizeEventInternal(_width, _height);
     }
     }
 
 
 	static double lastFrameTime = Game::getGameTime();
 	static double lastFrameTime = Game::getGameTime();
@@ -300,9 +314,15 @@ void Game::frame()
         // Update AI.
         // Update AI.
         _aiController->update(elapsedTime);
         _aiController->update(elapsedTime);
 
 
+        // Update gamepads.
+        Gamepad::updateInternal(elapsedTime);
+
         // Application Update.
         // Application Update.
         update(elapsedTime);
         update(elapsedTime);
 
 
+        // Update forms.
+        Form::updateInternal(elapsedTime);
+
         // Run script update.
         // Run script update.
         _scriptController->update(elapsedTime);
         _scriptController->update(elapsedTime);
 
 
@@ -326,9 +346,15 @@ void Game::frame()
     }
     }
 	else if (_state == Game::PAUSED)
 	else if (_state == Game::PAUSED)
     {
     {
+        // Update gamepads.
+        Gamepad::updateInternal(0);
+
         // Application Update.
         // Application Update.
         update(0);
         update(0);
 
 
+        // Update forms.
+        Form::updateInternal(0);
+
         // Script update.
         // Script update.
         _scriptController->update(0);
         _scriptController->update(0);
 
 
@@ -447,6 +473,10 @@ bool Game::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
     return false;
     return false;
 }
 }
 
 
+void Game::resizeEvent(unsigned int width, unsigned int height)
+{
+}
+
 bool Game::isGestureSupported(Gesture::GestureEvent evt)
 bool Game::isGestureSupported(Gesture::GestureEvent evt)
 {
 {
     return Platform::isGestureSupported(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)
 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);
     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.
      * Gets whether the current platform supports mouse input.
      *
      *
@@ -432,13 +443,15 @@ public:
     virtual void gestureTapEvent(int x, int y);
     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 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.
      * 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;
     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.
      * Sets multi-touch is to be enabled/disabled. Default is disabled.
      *
      *
@@ -493,6 +520,15 @@ public:
      */
      */
     inline void getAccelerometerValues(float* pitch, float* roll);
     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.
      * 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.
      * 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();
     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)
 inline void Game::setMultiTouch(bool enabled)
 {
 {
     Platform::setMultiTouch(enabled);
     Platform::setMultiTouch(enabled);

+ 67 - 9
gameplay/src/Gamepad.cpp

@@ -3,6 +3,8 @@
 #include "Game.h"
 #include "Game.h"
 #include "Button.h"
 #include "Button.h"
 #include "Platform.h"
 #include "Platform.h"
+#include "Form.h"
+#include "Joystick.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -23,6 +25,7 @@ Gamepad::Gamepad(const char* formPath)
     for (int i = 0; i < 2; ++i)
     for (int i = 0; i < 2; ++i)
     {
     {
         _uiJoysticks[i] = NULL;
         _uiJoysticks[i] = NULL;
+        _triggers[i] = 0.0f;
     }
     }
 
 
     for (int i = 0; i < 20; ++i)
     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,
 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)
                  unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
     : _handle(handle), _buttonCount(buttonCount), _joystickCount(joystickCount), _triggerCount(triggerCount),
     : _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()
 Gamepad::~Gamepad()
@@ -173,6 +189,19 @@ Gamepad* Gamepad::getGamepad(unsigned int index, bool preferPhysical)
     return backupVirtual;
     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)
 Gamepad::ButtonMapping Gamepad::getButtonMappingFromString(const char* string)
 {
 {
     if (strcmp(string, "A") == 0 || strcmp(string, "BUTTON_A") == 0)
     if (strcmp(string, "A") == 0 || strcmp(string, "BUTTON_A") == 0)
@@ -242,16 +271,18 @@ const char* Gamepad::getProductString() const
 
 
 void Gamepad::update(float elapsedTime)
 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;
     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_
 #ifndef GAMEPAD_H_
 #define GAMEPAD_H_
 #define GAMEPAD_H_
 
 
-#include "Form.h"
-#include "Joystick.h"
+#include "Vector2.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
 
 
-class Platform;
 class Button;
 class Button;
+class Container;
+class Form;
+class Joystick;
+class Platform;
 
 
 /**
 /**
  * Defines an interface for handling gamepad input.
  * Defines an interface for handling gamepad input.
@@ -17,7 +19,6 @@ class Gamepad
 {
 {
     friend class Platform;
     friend class Platform;
     friend class Game;
     friend class Game;
-    friend class Control;
     friend class Button;
     friend class Button;
 
 
 public:
 public:
@@ -28,7 +29,10 @@ public:
     enum GamepadEvent
     enum GamepadEvent
     {
     {
         CONNECTED_EVENT,
         CONNECTED_EVENT,
-        DISCONNECTED_EVENT
+        DISCONNECTED_EVENT,
+        BUTTON_EVENT,
+        JOYSTICK_EVENT,
+        TRIGGER_EVENT
     };
     };
 
 
     /**
     /**
@@ -164,7 +168,6 @@ public:
      */
      */
     void draw();
     void draw();
 
 
-
 private:
 private:
 
 
     /**
     /**
@@ -188,18 +191,23 @@ private:
      */
      */
     Gamepad(GamepadHandle handle, 
     Gamepad(GamepadHandle handle, 
             unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
             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.
      * Copy constructor.
      */
      */
     Gamepad(const Gamepad& copy);
     Gamepad(const Gamepad& copy);
 
 
+    /** 
+     * Destructor.
+     */
+    virtual ~Gamepad();
+
+    static void updateInternal(float elapsedTime);
+
     static Gamepad* add(GamepadHandle handle, 
     static Gamepad* add(GamepadHandle handle, 
                         unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
                         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);
     static Gamepad* add(const char* formPath);
 
 
@@ -211,20 +219,18 @@ private:
 
 
     static Gamepad* getGamepad(unsigned int index, bool preferPhysical = true);
     static Gamepad* getGamepad(unsigned int index, bool preferPhysical = true);
 
 
+    static Gamepad* getGamepad(GamepadHandle handle);
+
     static ButtonMapping getButtonMappingFromString(const char* string);
     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);
     void bindGamepadControls(Container* container);
 
 
-    static unsigned int getIndexFromMapping(Gamepad::ButtonMapping mapping);
-
     GamepadHandle _handle;        // The handle of the Gamepad.
     GamepadHandle _handle;        // The handle of the Gamepad.
     unsigned int _buttonCount;    // Number of buttons.
     unsigned int _buttonCount;    // Number of buttons.
     unsigned int _joystickCount;  // Number of joysticks.
     unsigned int _joystickCount;  // Number of joysticks.
@@ -233,13 +239,9 @@ private:
     unsigned int _productId;
     unsigned int _productId;
     std::string _vendorString;
     std::string _vendorString;
     std::string _productString;
     std::string _productString;
-    
-    // Data needed for virtual gamepads.
     Form* _form;
     Form* _form;
     Joystick* _uiJoysticks[2];
     Joystick* _uiJoysticks[2];
     Button* _uiButtons[20];
     Button* _uiButtons[20];
-
-    // Current gamepad state.
     unsigned int _buttons;
     unsigned int _buttons;
     Vector2 _joysticks[2];
     Vector2 _joysticks[2];
     float _triggers[2];
     float _triggers[2];

+ 1 - 1
gameplay/src/Image.h

@@ -66,7 +66,7 @@ private:
      * Constructor.
      * Constructor.
      */
      */
     Image();
     Image();
-        
+
     /**
     /**
      * Destructor.
      * 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)
 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);
     GP_ASSERT(copy);
     context.registerClonedNode(this, copy);
     context.registerClonedNode(this, copy);
     copy->_bindPose = _bindPose;
     copy->_bindPose = _bindPose;
-    copy->_skinCount = _skinCount;
     Node::cloneInto(copy, context);
     Node::cloneInto(copy, context);
     return copy;
     return copy;
 }
 }
@@ -35,6 +34,27 @@ Node::Type Joint::getType() const
     return Node::JOINT;
     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()
 void Joint::transformChanged()
 {
 {
     Node::transformChanged();
     Node::transformChanged();
@@ -47,7 +67,7 @@ void Joint::updateJointMatrix(const Matrix& bindShape, Vector4* matrixPalette)
     // the _jointMatrixDirty optimization since updateJointMatrix() may be
     // the _jointMatrixDirty optimization since updateJointMatrix() may be
     // called multiple times a frame with different bindShape matrices (and
     // called multiple times a frame with different bindShape matrices (and
     // different matrixPallete pointers).
     // different matrixPallete pointers).
-    if (_skinCount > 1 || _jointMatrixDirty)
+    if (_skin.next || _jointMatrixDirty)
     {
     {
         _jointMatrixDirty = false;
         _jointMatrixDirty = false;
 
 
@@ -73,4 +93,70 @@ void Joint::setInverseBindPose(const Matrix& m)
     _jointMatrixDirty = true;
     _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;
     Node::Type getType() const;
 
 
+    /**
+     * @see Node::getScene()
+     */
+    Scene* getScene() const;
+
     /**
     /**
      * Returns the inverse bind pose matrix for this joint.
      * Returns the inverse bind pose matrix for this joint.
      * 
      * 
@@ -85,6 +90,18 @@ protected:
 
 
 private:
 private:
 
 
+    /**
+     * Internal structure to track mesh skins referencing a joint.
+     */
+    struct SkinReference
+    {
+        MeshSkin* skin;
+        SkinReference* next;
+
+        SkinReference();
+        ~SkinReference();
+    };
+
     /**
     /**
      * Constructor.
      * Constructor.
      */
      */
@@ -95,22 +112,24 @@ private:
      */
      */
     Joint& operator=(const Joint&);
     Joint& operator=(const Joint&);
 
 
-protected:
+    void addSkin(MeshSkin* skin);
+
+    void removeSkin(MeshSkin* skin);
 
 
     /** 
     /** 
      * The Matrix representation of the Joint's bind pose.
      * The Matrix representation of the Joint's bind pose.
      */
      */
     Matrix _bindPose;
     Matrix _bindPose;
-    
-    /** 
+
+    /**
      * Flag used to mark if the Joint's matrix is dirty.
      * Flag used to mark if the Joint's matrix is dirty.
      */
      */
     bool _jointMatrixDirty;
     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)
 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.");
         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;
                 float dy = 0.0f;
 
 
                 _contactIndex = (int) contactIndex;
                 _contactIndex = (int) contactIndex;
-                notifyListeners(Listener::PRESS);
+                notifyListeners(Control::Listener::PRESS);
 
 
                 // Get the displacement of the touch from the centre.
                 // Get the displacement of the touch from the centre.
                 if (!_relative)
                 if (!_relative)
@@ -168,7 +168,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                 {
                     _value.set(value);
                     _value.set(value);
                     _dirty = true;
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
                 }
 
 
                 _state = ACTIVE;
                 _state = ACTIVE;
@@ -203,7 +203,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                 {
                     _value.set(value);
                     _value.set(value);
                     _dirty = true;
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
                 }
 
 
                 return _consumeInputEvents;
                 return _consumeInputEvents;
@@ -216,7 +216,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
             {
             {
                 _contactIndex = INVALID_CONTACT_INDEX;
                 _contactIndex = INVALID_CONTACT_INDEX;
 
 
-                notifyListeners(Listener::RELEASE);
+                notifyListeners(Control::Listener::RELEASE);
 
 
                 // Reset displacement and direction vectors.
                 // Reset displacement and direction vectors.
                 _displacement.set(0.0f, 0.0f);
                 _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);
                     _value.set(value);
                     _dirty = true;
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
                 }
 
 
                 _state = NORMAL;
                 _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)
 void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
 {
     GP_ASSERT(spriteBatch);
     GP_ASSERT(spriteBatch);
-    spriteBatch->start();
 
 
     // If the joystick is not absolute, then only draw if it is active.
     // If the joystick is not absolute, then only draw if it is active.
     if (!_relative || (_relative && _state == 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->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
 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->_id = id;
     label->setStyle(style);
     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;
     return label;
 }
 }
 
 
@@ -28,9 +34,8 @@ Label* Label::create(Theme::Style* style, Properties* properties)
 {
 {
     Label* label = new Label();
     Label* label = new Label();
     label->initialize(style, properties);
     label->initialize(style, properties);
+
     label->_consumeInputEvents = false;
     label->_consumeInputEvents = false;
-    
-    // Ensure that labels cannot receive focus.
     label->_focusIndex = -2;
     label->_focusIndex = -2;
 
 
     return label;
     return label;
@@ -41,7 +46,6 @@ void Label::initialize(Theme::Style* style, Properties* properties)
     GP_ASSERT(properties);
     GP_ASSERT(properties);
 
 
     Control::initialize(style, properties);
     Control::initialize(style, properties);
-
     const char* text = properties->getString("text");
     const char* text = properties->getString("text");
     if (text)
     if (text)
     {
     {
@@ -51,11 +55,11 @@ void Label::initialize(Theme::Style* style, Properties* properties)
 
 
 void Label::addListener(Control::Listener* listener, int eventFlags)
 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.");
         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.");
         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->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_viewportClipBounds);
         _font->finish();
         _font->finish();
     }
     }
-
-    _dirty = false;
 }
 }
 
 
 const char* Label::getType() const
 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);
     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
 Light::Type Light::getLightType() const
 {
 {
     return _type;
     return _type;
@@ -173,6 +252,9 @@ void Light::setRange(float range)
         GP_ERROR("Unsupported light type (%d).", _type);
         GP_ERROR("Unsupported light type (%d).", _type);
         break;
         break;
     }
     }
+
+    if (_node)
+        _node->setBoundsDirty();
 }
 }
 
 
 float Light::getRangeInverse() const
 float Light::getRangeInverse() const
@@ -221,6 +303,9 @@ void Light::setOuterAngle(float outerAngle)
 
 
     _spot->outerAngle = outerAngle;
     _spot->outerAngle = outerAngle;
     _spot->outerAngleCos = cos(outerAngle);
     _spot->outerAngleCos = cos(outerAngle);
+
+    if (_node)
+        _node->setBoundsDirty();
 }
 }
     
     
 float Light::getInnerAngleCos()  const
 float Light::getInnerAngleCos()  const

+ 14 - 0
gameplay/src/Light.h

@@ -3,6 +3,7 @@
 
 
 #include "Ref.h"
 #include "Ref.h"
 #include "Vector3.h"
 #include "Vector3.h"
+#include "Properties.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -103,6 +104,19 @@ public:
      */
      */
     static Light* createSpot(float red, float green, float blue, float range, float innerAngle, float outerAngle);
     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.
      * Destructor.
      */
      */

+ 5 - 0
gameplay/src/MaterialParameter.cpp

@@ -668,4 +668,9 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
     this->AnimationTarget::cloneInto(materialParameter, context);
     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
     class MethodBinding : public Ref
     {
     {
+        friend class RenderState;
+
     public:
     public:
+
         virtual void setValue(Effect* effect) = 0;
         virtual void setValue(Effect* effect) = 0;
 
 
     protected:
     protected:
+
+        /**
+         * Constructor.
+         */
+        MethodBinding(MaterialParameter* param);
+
         /**
         /**
          * Destructor.
          * Destructor.
          */
          */
@@ -256,6 +265,9 @@ private:
          * Hidden copy assignment operator.
          * Hidden copy assignment operator.
          */
          */
         MethodBinding& operator=(const MethodBinding&);
         MethodBinding& operator=(const MethodBinding&);
+
+        MaterialParameter* _parameter;
+        bool _autoBinding;
     };
     };
 
 
     /**
     /**
@@ -269,7 +281,6 @@ private:
         MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod);
         MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod);
         void setValue(Effect* effect);
         void setValue(Effect* effect);
     private:
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ClassType* _instance;
         ValueMethod _valueMethod;
         ValueMethod _valueMethod;
 
 
@@ -287,7 +298,6 @@ private:
         MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod);
         MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod);
         void setValue(Effect* effect);
         void setValue(Effect* effect);
     private:
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ClassType* _instance;
         ValueMethod _valueMethod;
         ValueMethod _valueMethod;
         CountMethod _countMethod;
         CountMethod _countMethod;
@@ -363,7 +373,7 @@ void MaterialParameter::bindValue(ClassType* classInstance, ParameterType (Class
 
 
 template <class ClassType, class ParameterType>
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodValueBinding<ClassType, ParameterType>::MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod) :
 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>
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodArrayBinding<ClassType, ParameterType>::MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod) :
 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,
      * 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.
      * 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 aspectRatio The aspect ratio, defined as view space width divided by height.
      * @param zNearPlane The distance to the near view plane.
      * @param zNearPlane The distance to the near view plane.
      * @param zFarPlane The distance to the far 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 "Base.h"
 #include "MeshBatch.h"
 #include "MeshBatch.h"
+#include "Material.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -42,6 +43,66 @@ MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveTy
     return batch;
     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()
 void MeshBatch::updateVertexAttributeBinding()
 {
 {
     GP_ASSERT(_material);
     GP_ASSERT(_material);
@@ -155,6 +216,11 @@ bool MeshBatch::resize(unsigned int capacity)
 
 
     return true;
     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()
 void MeshBatch::start()
 {
 {

+ 26 - 2
gameplay/src/MeshBatch.h

@@ -2,11 +2,12 @@
 #define MESHBATCH_H_
 #define MESHBATCH_H_
 
 
 #include "Mesh.h"
 #include "Mesh.h"
-#include "Material.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
 
 
+class Material;
+
 /**
 /**
  * Defines a class for rendering multiple mesh into a single draw call on the graphics device.
  * 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).
      * @param indexCount Number of indices (should be zero for non-indexed batches).
      */
      */
     template <class T>
     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.
      * Starts batching.
@@ -131,6 +153,8 @@ private:
      */
      */
     MeshBatch& operator=(const MeshBatch&);
     MeshBatch& operator=(const MeshBatch&);
 
 
+    void add(const void* vertices, size_t size, unsigned int vertexCount, const unsigned short* indices, unsigned int indexCount);
+
     void updateVertexAttributeBinding();
     void updateVertexAttributeBinding();
 
 
     bool resize(unsigned int capacity);
     bool resize(unsigned int capacity);

+ 2 - 57
gameplay/src/MeshBatch.inl

@@ -9,65 +9,10 @@ Material* MeshBatch::getMaterial() const
 }
 }
 
 
 template <class T>
 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());
     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])
     if (_joints[index])
     {
     {
-        _joints[index]->_skinCount--;
+        _joints[index]->removeSkin(this);
         SAFE_RELEASE(_joints[index]);
         SAFE_RELEASE(_joints[index]);
     }
     }
 
 
@@ -150,7 +150,7 @@ void MeshSkin::setJoint(Joint* joint, unsigned int index)
     if (joint)
     if (joint)
     {
     {
         joint->addRef();
         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 Model;
     friend class Joint;
     friend class Joint;
     friend class Node;
     friend class Node;
+    friend class Scene;
 
 
 public:
 public:
 
 

+ 1 - 0
gameplay/src/Model.h

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

+ 42 - 8
gameplay/src/Node.cpp

@@ -126,6 +126,8 @@ void Node::addChild(Node* child)
 
 
     ++_childCount;
     ++_childCount;
 
 
+    setBoundsDirty();
+
     if (_notifyHierarchyChanged)
     if (_notifyHierarchyChanged)
     {
     {
         hierarchyChanged();
         hierarchyChanged();
@@ -376,13 +378,15 @@ unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool rec
 
 
 Scene* Node::getScene() const
 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;
     return NULL;
@@ -766,6 +770,8 @@ void Node::setLight(Light* light)
             _light->addRef();
             _light->addRef();
             _light->setNode(this);
             _light->setNode(this);
         }
         }
+
+        setBoundsDirty();
     }
     }
 }
 }
 
 
@@ -816,6 +822,8 @@ void Node::setTerrain(Terrain* terrain)
             _terrain->addRef();
             _terrain->addRef();
             _terrain->setNode(this);
             _terrain->setNode(this);
         }
         }
+
+        setBoundsDirty();
     }
     }
 }
 }
 
 
@@ -872,7 +880,27 @@ const BoundingSphere& Node::getBoundingSphere() const
                 _bounds.merge(_model->getMesh()->getBoundingSphere());
                 _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
             // Empty bounding sphere, set the world translation with zero radius
             worldMatrix.getTranslation(&_bounds.center);
             worldMatrix.getTranslation(&_bounds.center);
@@ -933,7 +961,6 @@ const BoundingSphere& Node::getBoundingSphere() const
     return _bounds;
     return _bounds;
 }
 }
 
 
-
 Node* Node::clone() const
 Node* Node::clone() const
 {
 {
     NodeCloneContext context;
     NodeCloneContext context;
@@ -953,11 +980,13 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
     Node* copy = cloneSingleNode(context);
     Node* copy = cloneSingleNode(context);
     GP_ASSERT(copy);
     GP_ASSERT(copy);
 
 
+    // Find our current last child
     Node* lastChild = NULL;
     Node* lastChild = NULL;
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     {
     {
         lastChild = child;
         lastChild = child;
     }
     }
+
     // Loop through the nodes backwards because addChild adds the node to the front.
     // Loop through the nodes backwards because addChild adds the node to the front.
     for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     {
     {
@@ -966,6 +995,7 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
         copy->addChild(childCopy);
         copy->addChild(childCopy);
         childCopy->release();
         childCopy->release();
     }
     }
+
     return copy;
     return copy;
 }
 }
 
 
@@ -1002,6 +1032,10 @@ void Node::cloneInto(Node* node, NodeCloneContext &context) const
     }
     }
     node->_world = _world;
     node->_world = _world;
     node->_bounds = _bounds;
     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)
     if (_tags)
     {
     {
         node->_tags = new std::map<std::string, std::string>(_tags->begin(), _tags->end());
         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 Scene;
     friend class Bundle;
     friend class Bundle;
     friend class MeshSkin;
     friend class MeshSkin;
+    friend class Light;
 
 
 public:
 public:
 
 
@@ -223,7 +224,7 @@ public:
      *
      *
      * @return The scene.
      * @return The scene.
      */
      */
-    Scene* getScene() const;
+    virtual Scene* getScene() const;
 
 
     /**
     /**
      * Gets the top level node in this node's parent hierarchy.
      * 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()
 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);
     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.
      * Set if multi-touch is enabled on the platform.
      *
      *
@@ -195,6 +207,14 @@ private:
      * @param roll The accelerometer roll.
      * @param roll The accelerometer roll.
      */
      */
     static void getAccelerometerValues(float* pitch, float* 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).
      * 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);
     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.
      * Internal method used only from static code in various platform implementation.
      *
      *
      * @script{ignore}
      * @script{ignore}
@@ -281,7 +315,8 @@ public:
     static void gamepadEventConnectedInternal(GamepadHandle handle, unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
     static void gamepadEventConnectedInternal(GamepadHandle handle, unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
                                               unsigned int vendorId, unsigned int productId, 
                                               unsigned int vendorId, unsigned int productId, 
                                               const char* vendorString, const char* productString);
                                               const char* vendorString, const char* productString);
-   /**
+
+    /**
      * Internal method used only from static code in various platform implementation.
      * Internal method used only from static code in various platform implementation.
      *
      *
      * @script{ignore}
      * @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 <sys/time.h>
 #include <GL/glxew.h>
 #include <GL/glxew.h>
 #include <poll.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 TOUCH_COUNT_MAX     4
+#define MAX_GAMEPADS 4
 
 
 using namespace std;
 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;
 struct timespec __timespec;
 static double __timeStart;
 static double __timeStart;
 static double __timeAbsolute;
 static double __timeAbsolute;
@@ -27,6 +85,7 @@ static float __roll;
 static bool __mouseCaptured = false;
 static bool __mouseCaptured = false;
 static float __mouseCapturePointX = 0;
 static float __mouseCapturePointX = 0;
 static float __mouseCapturePointY = 0;
 static float __mouseCapturePointY = 0;
+static bool __multiSampling = false;
 static bool __cursorVisible = true;
 static bool __cursorVisible = true;
 static Display* __display;
 static Display* __display;
 static Window   __window;
 static Window   __window;
@@ -34,1147 +93,1555 @@ static int __windowSize[2];
 static GLXContext __context;
 static GLXContext __context;
 static Window __attachToWindow;
 static Window __attachToWindow;
 static Atom __atomWmDeleteWindow;
 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)
     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.
  * 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)
     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 HGLRC __hrc = 0;
 static bool __mouseCaptured = false;
 static bool __mouseCaptured = false;
 static POINT __mouseCapturePoint = { 0, 0 };
 static POINT __mouseCapturePoint = { 0, 0 };
+static bool __multiSampling = false;
 static bool __cursorVisible = true;
 static bool __cursorVisible = true;
 static unsigned int __gamepadsConnected = 0;
 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_BUTTON_COUNT = 14;
 static const unsigned int XINPUT_JOYSTICK_COUNT = 2;
 static const unsigned int XINPUT_JOYSTICK_COUNT = 2;
 static const unsigned int XINPUT_TRIGGER_COUNT = 2;
 static const unsigned int XINPUT_TRIGGER_COUNT = 2;
-#endif
 
 
-#ifdef USE_XINPUT
 static XINPUT_STATE __xInputState;
 static XINPUT_STATE __xInputState;
 static bool __connectedXInput[4];
 static bool __connectedXInput[4];
 
 
@@ -501,6 +500,11 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
 
     case WM_KILLFOCUS:
     case WM_KILLFOCUS:
         break;
         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); 
     return DefWindowProc(hwnd, msg, wParam, lParam); 
@@ -515,6 +519,7 @@ struct WindowCreationParams
     RECT rect;
     RECT rect;
     std::wstring windowName;
     std::wstring windowName;
     bool fullscreen;
     bool fullscreen;
+    bool resizable;
     int samples;
     int samples;
 };
 };
 
 
@@ -551,6 +556,7 @@ Platform::~Platform()
 bool createWindow(WindowCreationParams* params, HWND* hwnd, HDC* hdc)
 bool createWindow(WindowCreationParams* params, HWND* hwnd, HDC* hdc)
 {
 {
     bool fullscreen = false;
     bool fullscreen = false;
+    bool resizable = false;
     RECT rect = { CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT };
     RECT rect = { CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT };
     std::wstring windowName;
     std::wstring windowName;
     if (params)
     if (params)
@@ -558,11 +564,25 @@ bool createWindow(WindowCreationParams* params, HWND* hwnd, HDC* hdc)
         windowName = params->windowName;
         windowName = params->windowName;
         memcpy(&rect, &params->rect, sizeof(RECT));
         memcpy(&rect, &params->rect, sizeof(RECT));
         fullscreen = params->fullscreen;
         fullscreen = params->fullscreen;
+        resizable = params->resizable;
     }
     }
 
 
     // Set the window style.
     // 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.
     // Adjust the window rectangle so the client size is the requested size.
     AdjustWindowRectEx(&rect, style, FALSE, styleEx);
     AdjustWindowRectEx(&rect, style, FALSE, styleEx);
@@ -663,6 +683,8 @@ bool initializeGL(WindowCreationParams* params)
         WGL_STENCIL_BITS_ARB, DEFAULT_STENCIL_BUFFER_SIZE,
         WGL_STENCIL_BITS_ARB, DEFAULT_STENCIL_BUFFER_SIZE,
         0
         0
     };
     };
+    __multiSampling = params->samples > 0;
+
     UINT numFormats;
     UINT numFormats;
     if (!wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats) || numFormats == 0)
     if (!wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats) || numFormats == 0)
     {
     {
@@ -682,6 +704,8 @@ bool initializeGL(WindowCreationParams* params)
                     break;
                     break;
                 }
                 }
             }
             }
+
+            __multiSampling = params->samples > 0;
         }
         }
 
 
         if (!valid)
         if (!valid)
@@ -761,6 +785,7 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     // Read window settings from config.
     // Read window settings from config.
     WindowCreationParams params;
     WindowCreationParams params;
     params.fullscreen = false;
     params.fullscreen = false;
+    params.resizable = false;
     params.rect.left = 0;
     params.rect.left = 0;
     params.rect.top = 0;
     params.rect.top = 0;
     params.rect.right = 0;
     params.rect.right = 0;
@@ -784,6 +809,8 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
 
             // Read fullscreen state.
             // Read fullscreen state.
             params.fullscreen = config->getBool("fullscreen");
             params.fullscreen = config->getBool("fullscreen");
+            // Read resizable state.
+            params.resizable = config->getBool("resizable");
             // Read multisampling state.
             // Read multisampling state.
             params.samples = config->getInt("samples");
             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");
                 Platform::gamepadEventConnectedInternal(i, XINPUT_BUTTON_COUNT, XINPUT_JOYSTICK_COUNT, XINPUT_TRIGGER_COUNT, 0, 0, "Microsoft", "XBox360 Controller");
                 __connectedXInput[i] = true;
                 __connectedXInput[i] = true;
             }
             }
-
         }
         }
     }
     }
 #endif
 #endif
@@ -1064,6 +1090,30 @@ void Platform::sleep(long ms)
     Sleep(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)
 void Platform::setMultiTouch(bool enabled)
 {
 {
     // not supported
     // not supported
@@ -1083,6 +1133,14 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
     *roll = __roll;
     *roll = __roll;
 }
 }
 
 
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
+
 bool Platform::hasMouse()
 bool Platform::hasMouse()
 {
 {
     return true;
     return true;
@@ -1184,13 +1242,15 @@ void Platform::pollGamepadState(Gamepad* gamepad)
         };
         };
 
 
         const unsigned int *mapping = xInputMapping;
         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)
             if (buttons & 1)
             {
             {
-                gamepad->_buttons |= (1 << *mapping);
+                mappedButtons |= (1 << *mapping);
             }
             }
         }
         }
+        gamepad->setButtons(mappedButtons);
 
 
         unsigned int i;
         unsigned int i;
         for (i = 0; i < gamepad->_joystickCount; ++i)
         for (i = 0; i < gamepad->_joystickCount; ++i)
@@ -1211,7 +1271,7 @@ void Platform::pollGamepadState(Gamepad* gamepad)
                 break;
                 break;
             }
             }
 
 
-            gamepad->_joysticks[i].set(x, y);
+            gamepad->setJoystickValue(i, x, y);
         }
         }
 
 
         for (i = 0; i < gamepad->_triggerCount; ++i)
         for (i = 0; i < gamepad->_triggerCount; ++i)
@@ -1231,67 +1291,19 @@ void Platform::pollGamepadState(Gamepad* gamepad)
 
 
             if (trigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
             if (trigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
             {
             {
-                gamepad->_triggers[i] = 0.0f;
+                gamepad->setTriggerValue(i, 0.0f);
             }
             }
             else
             else
             {
             {
-                gamepad->_triggers[i] = (float)trigger / 255.0f;
+                gamepad->setTriggerValue(i, (float)trigger / 255.0f);
             }
             }
         }
         }
     }
     }
 }
 }
 #else
 #else
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-    // TODO: Support generic HID gamepads (including XBox controllers) without requiring XInput.
-}
+void Platform::pollGamepadState(Gamepad* gamepad) { }
 #endif
 #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()
 void Platform::shutdownInternal()
 {
 {
     Game::getInstance()->shutdown();
     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_HEIGHT = [[UIScreen mainScreen] bounds].size.width * [[UIScreen mainScreen] scale];
 extern const int WINDOW_SCALE = [[UIScreen mainScreen] scale];
 extern const int WINDOW_SCALE = [[UIScreen mainScreen] scale];
 
 
+int __argc = 0;
+char** __argv = 0;
+
 @class AppDelegate;
 @class AppDelegate;
 @class View;
 @class View;
 
 
@@ -280,6 +283,8 @@ int getUnicode(int key);
             samples /= 2;
             samples /= 2;
         }
         }
         
         
+        //todo: __multiSampling = samples > 0;
+
         // Re-bind the default framebuffer
         // Re-bind the default framebuffer
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
         
         
@@ -1379,6 +1384,14 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
 {
     [__appDelegate getAccelerometerPitch:pitch roll:roll];
     [__appDelegate getAccelerometerPitch:pitch roll:roll];
 }
 }
+    
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
 
 
 bool Platform::hasMouse()
 bool Platform::hasMouse()
 {
 {
@@ -1408,6 +1421,16 @@ bool Platform::isCursorVisible()
     return false;
     return false;
 }
 }
 
 
+void Platform::setMultiSampling(bool enabled)
+{
+    //todo
+}
+
+bool Platform::isMultiSampling()
+{
+    return false; //todo
+}
+
 void Platform::setMultiTouch(bool enabled) 
 void Platform::setMultiTouch(bool enabled) 
 {
 {
     __view.multipleTouchEnabled = 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()
 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)
 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.");
         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);
                         RadioButton::clearSelected(_groupId);
                         _selected = true;
                         _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);
     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)
 void RadioButton::clearSelected(const std::string& groupId)
 {
 {
     std::vector<RadioButton*>::const_iterator it;
     std::vector<RadioButton*>::const_iterator it;
@@ -127,7 +148,7 @@ void RadioButton::clearSelected(const std::string& groupId)
         {
         {
             radioButton->_selected = false;
             radioButton->_selected = false;
             radioButton->_dirty = true;
             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);
     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
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * 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;
     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}
  * @script{ignore}
  */
  */
@@ -208,94 +222,186 @@ void RenderState::setNodeBinding(Node* node)
 
 
 void RenderState::applyAutoBinding(const char* uniformName, const char* autoBinding)
 void RenderState::applyAutoBinding(const char* uniformName, const char* autoBinding)
 {
 {
+    GP_ASSERT(_nodeBinding);
+
     MaterialParameter* param = getParameter(uniformName);
     MaterialParameter* param = getParameter(uniformName);
     GP_ASSERT(param);
     GP_ASSERT(param);
 
 
+    bool bound = false;
+
     // First attempt to resolve the binding using custom registered resolvers.
     // First attempt to resolve the binding using custom registered resolvers.
     if (_customAutoBindingResolvers.size() > 0)
     if (_customAutoBindingResolvers.size() > 0)
     {
     {
         for (size_t i = 0, count = _customAutoBindingResolvers.size(); i < count; ++i)
         for (size_t i = 0, count = _customAutoBindingResolvers.size(); i < count; ++i)
         {
         {
             if (_customAutoBindingResolvers[i](autoBinding, _nodeBinding, param))
             if (_customAutoBindingResolvers[i](autoBinding, _nodeBinding, param))
-                return; // handled by custom resolver
+            {
+                // Handled by custom auto binding resolver
+                bound = true;
+                break;
+            }
         }
         }
     }
     }
 
 
     // Perform built-in resolution
     // 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)
 void RenderState::bind(Pass* pass)
 {
 {
     GP_ASSERT(pass);
     GP_ASSERT(pass);
@@ -359,6 +465,7 @@ void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context)
 {
 {
     GP_ASSERT(renderState);
     GP_ASSERT(renderState);
 
 
+    // Clone parameters
     for (std::map<std::string, std::string>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
     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());
         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;
         const MaterialParameter* param = *it;
         GP_ASSERT(param);
         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());
         MaterialParameter* paramCopy = new MaterialParameter(param->getName());
         param->cloneInto(paramCopy);
         param->cloneInto(paramCopy);
 
 
         renderState->_parameters.push_back(paramCopy);
         renderState->_parameters.push_back(paramCopy);
     }
     }
-    renderState->_parent = _parent;
+
+    // Clone our state block
     if (_state)
     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()
 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)
 static bool parseBoolean(const char* value)
 {
 {
     GP_ASSERT(value);
     GP_ASSERT(value);

+ 34 - 0
gameplay/src/RenderState.h

@@ -2,6 +2,8 @@
 #define RENDERSTATE_H_
 #define RENDERSTATE_H_
 
 
 #include "Ref.h"
 #include "Ref.h"
+#include "Vector3.h"
+#include "Vector4.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -285,6 +287,8 @@ public:
 
 
         static void enableDepthWrite();
         static void enableDepthWrite();
 
 
+        void cloneInto(StateBlock* state);
+
         // States
         // States
         bool _cullFaceEnabled;
         bool _cullFaceEnabled;
         bool _depthTestEnabled;
         bool _depthTestEnabled;
@@ -304,12 +308,25 @@ public:
      * The returned MaterialParameter can be used to set values for the specified
      * The returned MaterialParameter can be used to set values for the specified
      * parameter name.
      * 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.
      * @param name Material parameter (uniform) name.
      * 
      * 
      * @return A MaterialParameter for the specified name.
      * @return A MaterialParameter for the specified name.
      */
      */
     MaterialParameter* getParameter(const char* name) const;
     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.
      * Sets a material parameter auto-binding.
      *
      *
@@ -461,6 +478,23 @@ private:
      */
      */
     RenderState& operator=(const RenderState&);
     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:
 protected:
 
 
     /**
     /**

+ 82 - 0
gameplay/src/Scene.cpp

@@ -5,6 +5,7 @@
 #include "MeshSkin.h"
 #include "MeshSkin.h"
 #include "Joint.h"
 #include "Joint.h"
 #include "Terrain.h"
 #include "Terrain.h"
+#include "Bundle.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -12,6 +13,52 @@ namespace gameplay
 // Global list of active scenes
 // Global list of active scenes
 static std::vector<Scene*> __sceneList;
 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)
 Scene::Scene(const char* id)
     : _id(id ? id : ""), _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), 
     : _id(id ? id : ""), _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), 
     _lightColor(1,1,1), _lightDirection(0,-1,0), _bindAudioListenerToCamera(true), _debugBatch(NULL)
     _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)
 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);
     return SceneLoader::load(filePath);
 }
 }
 
 
@@ -136,6 +194,30 @@ unsigned int Scene::findNodes(const char* id, std::vector<Node*>& nodes, bool re
     return count;
     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* Scene::addNode(const char* id)
 {
 {
     Node* node = Node::create(id);
     Node* node = Node::create(id);

+ 19 - 17
gameplay/src/Scene.h

@@ -36,9 +36,9 @@ public:
     static Scene* create(const char* id = NULL);
     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
      * @return The loaded scene or <code>NULL</code> if the scene
      *      could not be loaded from the given file.
      *      could not be loaded from the given file.
      * @script{create}
      * @script{create}
@@ -333,7 +333,7 @@ private:
     /**
     /**
      * Visits the given node and all of its children recursively.
      * 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;
     std::string _id;
     Camera* _activeCamera;
     Camera* _activeCamera;
@@ -356,7 +356,6 @@ void Scene::visit(T* instance, bool (T::*visitMethod)(Node*))
     }
     }
 }
 }
 
 
-
 template <class T, class C>
 template <class T, class C>
 void Scene::visit(T* instance, bool (T::*visitMethod)(Node*,C), C cookie)
 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))
     if (!(instance->*visitMethod)(node))
         return;
         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.
     // Recurse for all children.
     for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
     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))
     if (!(instance->*visitMethod)(node, cookie))
         return;
         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.
     // Recurse for all children.
     for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
     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 "Bundle.h"
 #include "SceneLoader.h"
 #include "SceneLoader.h"
 #include "Terrain.h"
 #include "Terrain.h"
+#include "Light.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -81,6 +82,7 @@ Scene* SceneLoader::loadInternal(const char* url)
         SceneNodeProperty::MATERIAL | 
         SceneNodeProperty::MATERIAL | 
         SceneNodeProperty::PARTICLE |
         SceneNodeProperty::PARTICLE |
         SceneNodeProperty::TERRAIN |
         SceneNodeProperty::TERRAIN |
+        SceneNodeProperty::LIGHT |
         SceneNodeProperty::CAMERA |
         SceneNodeProperty::CAMERA |
         SceneNodeProperty::ROTATE |
         SceneNodeProperty::ROTATE |
         SceneNodeProperty::SCALE |
         SceneNodeProperty::SCALE |
@@ -211,6 +213,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::PARTICLE ||
         snp._type == SceneNodeProperty::PARTICLE ||
         snp._type == SceneNodeProperty::TERRAIN ||
         snp._type == SceneNodeProperty::TERRAIN ||
+        snp._type == SceneNodeProperty::LIGHT ||
         snp._type == SceneNodeProperty::CAMERA ||
         snp._type == SceneNodeProperty::CAMERA ||
         snp._type == SceneNodeProperty::COLLISION_OBJECT)
         snp._type == SceneNodeProperty::COLLISION_OBJECT)
     {
     {
@@ -261,6 +264,13 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             SAFE_RELEASE(terrain);
             SAFE_RELEASE(terrain);
             break;
             break;
         }
         }
+        case SceneNodeProperty::LIGHT:
+        {
+            Light* light = Light::create(p);
+            node->setLight(light);
+            SAFE_RELEASE(light);
+            break;
+        }
         case SceneNodeProperty::CAMERA:
         case SceneNodeProperty::CAMERA:
         {
         {
             Camera* camera = Camera::create(p);
             Camera* camera = Camera::create(p);
@@ -575,6 +585,12 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::TERRAIN, propertyUrl.c_str());
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::TERRAIN, propertyUrl.c_str());
                     _properties[propertyUrl] = subns;
                     _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)
                 else if (strcmp(subns->getNamespace(), "camera") == 0)
                 {
                 {
                     propertyUrl += "camera/" + std::string(subns->getId());
                     propertyUrl += "camera/" + std::string(subns->getId());
@@ -631,6 +647,10 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::TERRAIN, ns->getString());
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::TERRAIN, ns->getString());
                 }
                 }
+                else if (strcmp(name, "light") == 0)
+                {
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::LIGHT, ns->getString());
+                }
                 else if (strcmp(name, "camera") == 0)
                 else if (strcmp(name, "camera") == 0)
                 {
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::CAMERA, ns->getString());
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::CAMERA, ns->getString());

+ 7 - 6
gameplay/src/SceneLoader.h

@@ -50,12 +50,13 @@ private:
             MATERIAL = 2,
             MATERIAL = 2,
             PARTICLE = 4,
             PARTICLE = 4,
             TERRAIN = 8,
             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);
         SceneNodeProperty(Type type, const std::string& url, int index);

+ 243 - 78
gameplay/src/ScriptController.cpp

@@ -41,6 +41,92 @@
     \
     \
     return arr
     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
 namespace gameplay
 {
 {
 
 
@@ -340,17 +426,33 @@ bool ScriptUtil::luaCheckBool(lua_State* state, int n)
 
 
 void ScriptController::loadScript(const char* path, bool forceReload)
 void ScriptController::loadScript(const char* path, bool forceReload)
 {
 {
+    GP_ASSERT(path);
     std::set<std::string>::iterator iter = _loadedScripts.find(path);
     std::set<std::string>::iterator iter = _loadedScripts.find(path);
     if (iter == _loadedScripts.end() || forceReload)
     if (iter == _loadedScripts.end() || forceReload)
     {
     {
+#ifdef __ANDROID__
         const char* scriptContents = FileSystem::readAll(path);
         const char* scriptContents = FileSystem::readAll(path);
         if (luaL_dostring(_lua, scriptContents))
         if (luaL_dostring(_lua, scriptContents))
+        {
             GP_WARN("Failed to run Lua script with error: '%s'.", lua_tostring(_lua, -1));
             GP_WARN("Failed to run Lua script with error: '%s'.", lua_tostring(_lua, -1));
-
+        }
         SAFE_DELETE_ARRAY(scriptContents);
         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())
         if (iter == _loadedScripts.end())
+        {
             _loadedScripts.insert(path);
             _loadedScripts.insert(path);
+        }
     }
     }
 }
 }
 
 
@@ -377,97 +479,97 @@ std::string ScriptController::loadUrl(const char* url)
 
 
 bool ScriptController::getBool(const char* name, bool defaultValue)
 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;
     bool b = lua_isboolean(_lua, -1) ? ScriptUtil::luaCheckBool(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return b;
     return b;
 }
 }
 
 
 char ScriptController::getChar(const char* name, char defaultValue)
 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;
     char c = lua_isnumber(_lua, -1) ?  (char)luaL_checkint(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return c;
     return c;
 }
 }
 
 
 short ScriptController::getShort(const char* name, short defaultValue)
 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;
     short n = lua_isnumber(_lua, -1) ? (short)luaL_checkint(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
     return n;
 }
 }
 
 
 int ScriptController::getInt(const char* name, int defaultValue)
 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;
     int n = lua_isnumber(_lua, -1) ? luaL_checkint(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
     return n;
 }
 }
 
 
 long ScriptController::getLong(const char* name, long defaultValue)
 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;
     long n = lua_isnumber(_lua, -1) ? luaL_checklong(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
     return n;
 }
 }
 
 
 unsigned char ScriptController::getUnsignedChar(const char* name, unsigned char defaultValue)
 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;
     unsigned char c = lua_isnumber(_lua, -1) ? (unsigned char)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return c;
     return c;
 }
 }
 
 
 unsigned short ScriptController::getUnsignedShort(const char* name, unsigned short defaultValue)
 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;
     unsigned short n = lua_isnumber(_lua, -1) ? (unsigned short)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
     return n;
 }
 }
 
 
 unsigned int ScriptController::getUnsignedInt(const char* name, unsigned int defaultValue)
 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;
     unsigned int n = lua_isnumber(_lua, -1) ? (unsigned int)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
     return n;
 }
 }
 
 
 unsigned long ScriptController::getUnsignedLong(const char* name, unsigned long defaultValue)
 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;
     unsigned long n = lua_isnumber(_lua, -1) ? (unsigned long)luaL_checkunsigned(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
     return n;
 }
 }
 
 
 float ScriptController::getFloat(const char* name, float defaultValue)
 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;
     float f = lua_isnumber(_lua, -1) ? (float)luaL_checknumber(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return f;
     return f;
 }
 }
 
 
 double ScriptController::getDouble(const char* name, double defaultValue)
 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;
     double n = lua_isnumber(_lua, -1) ? (double)luaL_checknumber(_lua, -1) : defaultValue;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return n;
     return n;
 }
 }
 
 
 const char* ScriptController::getString(const char* name)
 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;
     const char* s = lua_isstring(_lua, -1) ? luaL_checkstring(_lua, -1) : NULL;
-    lua_pop(_lua, 1);
+    POP_NESTED_VARIABLE();
     return s;
     return s;
 }
 }
 
 
@@ -555,15 +657,10 @@ void ScriptController::print(const char* str1, const char* str2)
 
 
 ScriptController::ScriptController() : _lua(NULL)
 ScriptController::ScriptController() : _lua(NULL)
 {
 {
-    memset(_callbacks, 0, sizeof(std::string*) * CALLBACK_COUNT);
 }
 }
 
 
 ScriptController::~ScriptController()
 ScriptController::~ScriptController()
 {
 {
-    for (unsigned int i = 0; i < CALLBACK_COUNT; i++)
-    {
-        SAFE_DELETE(_callbacks[i]);
-    }
 }
 }
 
 
 static const char* lua_print_function = 
 static const char* lua_print_function = 
@@ -571,7 +668,6 @@ static const char* lua_print_function =
     "    ScriptController.print(table.concat({...},\"\\t\"), \"\\n\")\n"
     "    ScriptController.print(table.concat({...},\"\\t\"), \"\\n\")\n"
     "end\n";
     "end\n";
 
 
-#ifndef WIN32 
 static const char* lua_loadfile_function = 
 static const char* lua_loadfile_function = 
     "do\n"
     "do\n"
     "    local oldLoadfile = loadfile\n"
     "    local oldLoadfile = loadfile\n"
@@ -595,7 +691,6 @@ static const char* lua_dofile_function =
     "        return oldDofile(filename)\n"
     "        return oldDofile(filename)\n"
     "    end\n"
     "    end\n"
     "end\n";
     "end\n";
-#endif
 
 
 void ScriptController::initialize()
 void ScriptController::initialize()
 {
 {
@@ -606,27 +701,25 @@ void ScriptController::initialize()
 
 
 #ifndef NO_LUA_BINDINGS
 #ifndef NO_LUA_BINDINGS
     lua_RegisterAllBindings();
     lua_RegisterAllBindings();
+    ScriptUtil::registerFunction("convert", ScriptController::convert);
 #endif
 #endif
 
 
     // Create our own print() function that uses gameplay::print.
     // Create our own print() function that uses gameplay::print.
     if (luaL_dostring(_lua, lua_print_function))
     if (luaL_dostring(_lua, lua_print_function))
         GP_ERROR("Failed to load custom print() function with error: '%s'.", lua_tostring(_lua, -1));
         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.
     // Change the functions that read a file to use FileSystem.getResourcePath as their base path.
     if (luaL_dostring(_lua, lua_loadfile_function))
     if (luaL_dostring(_lua, lua_loadfile_function))
         GP_ERROR("Failed to load custom loadfile() function with error: '%s'.", lua_tostring(_lua, -1));
         GP_ERROR("Failed to load custom loadfile() function with error: '%s'.", lua_tostring(_lua, -1));
     if (luaL_dostring(_lua, lua_dofile_function))
     if (luaL_dostring(_lua, lua_dofile_function))
         GP_ERROR("Failed to load custom dofile() function with error: '%s'.", lua_tostring(_lua, -1));
         GP_ERROR("Failed to load custom dofile() function with error: '%s'.", lua_tostring(_lua, -1));
-#endif
 }
 }
 
 
 void ScriptController::initializeGame()
 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()
 void ScriptController::finalize()
@@ -640,21 +733,15 @@ void ScriptController::finalize()
 
 
 void ScriptController::finalizeGame()
 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
 	// Remove any registered callbacks so they don't get called after shutdown
 	for (unsigned int i = 0; i < CALLBACK_COUNT; i++)
 	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.
     // Perform a full garbage collection cycle.
 	// Note that this does NOT free any global variables declared in scripts, since 
 	// 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)
 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)
 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)
 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)
 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)
 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;
     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)
 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;
         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;
     int argumentCount = 0;
-    lua_getglobal(_lua, func);
 
 
     // Push the arguments to the Lua stack if there are any.
     // Push the arguments to the Lua stack if there are any.
     if (sig)
     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));
         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)
 ScriptController::ScriptCallback ScriptController::toCallback(const char* name)
@@ -852,6 +970,8 @@ ScriptController::ScriptCallback ScriptController::toCallback(const char* name)
         return ScriptController::RENDER;
         return ScriptController::RENDER;
     else if (strcmp(name, "finalize") == 0)
     else if (strcmp(name, "finalize") == 0)
         return ScriptController::FINALIZE;
         return ScriptController::FINALIZE;
+    else if (strcmp(name, "resizeEvent") == 0)
+        return ScriptController::RESIZE_EVENT;
     else if (strcmp(name, "keyEvent") == 0)
     else if (strcmp(name, "keyEvent") == 0)
         return ScriptController::KEY_EVENT;
         return ScriptController::KEY_EVENT;
     else if (strcmp(name, "touchEvent") == 0)
     else if (strcmp(name, "touchEvent") == 0)
@@ -864,31 +984,74 @@ ScriptController::ScriptCallback ScriptController::toCallback(const char* name)
         return ScriptController::INVALID_CALLBACK;
         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.
 // Helper macros.
 #define SCRIPT_EXECUTE_FUNCTION_NO_PARAM(type, checkfunc) \
 #define SCRIPT_EXECUTE_FUNCTION_NO_PARAM(type, checkfunc) \
+    int top = lua_gettop(_lua); \
     executeFunctionHelper(1, func, NULL, NULL); \
     executeFunctionHelper(1, func, NULL, NULL); \
     type value = (type)checkfunc(_lua, -1); \
     type value = (type)checkfunc(_lua, -1); \
     lua_pop(_lua, -1); \
     lua_pop(_lua, -1); \
+    lua_settop(_lua, top); \
     return value;
     return value;
 
 
 #define SCRIPT_EXECUTE_FUNCTION_PARAM(type, checkfunc) \
 #define SCRIPT_EXECUTE_FUNCTION_PARAM(type, checkfunc) \
+    int top = lua_gettop(_lua); \
     va_list list; \
     va_list list; \
     va_start(list, args); \
     va_start(list, args); \
     executeFunctionHelper(1, func, args, &list); \
     executeFunctionHelper(1, func, args, &list); \
     type value = (type)checkfunc(_lua, -1); \
     type value = (type)checkfunc(_lua, -1); \
     lua_pop(_lua, -1); \
     lua_pop(_lua, -1); \
     va_end(list); \
     va_end(list); \
+    lua_settop(_lua, top); \
     return value;
     return value;
 
 
 #define SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(type, checkfunc) \
 #define SCRIPT_EXECUTE_FUNCTION_PARAM_LIST(type, checkfunc) \
+    int top = lua_gettop(_lua); \
     executeFunctionHelper(1, func, args, list); \
     executeFunctionHelper(1, func, args, list); \
     type value = (type)checkfunc(_lua, -1); \
     type value = (type)checkfunc(_lua, -1); \
     lua_pop(_lua, -1); \
     lua_pop(_lua, -1); \
+    lua_settop(_lua, top); \
     return value;
     return value;
 
 
 template<> void ScriptController::executeFunction<void>(const char* func)
 template<> void ScriptController::executeFunction<void>(const char* func)
 {
 {
+    int top = lua_gettop(_lua);
     executeFunctionHelper(0, func, NULL, NULL);
     executeFunctionHelper(0, func, NULL, NULL);
+    lua_settop(_lua, top);
 }
 }
 
 
 template<> bool ScriptController::executeFunction<bool>(const char* func)
 template<> bool ScriptController::executeFunction<bool>(const char* func)
@@ -954,10 +1117,12 @@ template<> std::string ScriptController::executeFunction<std::string>(const char
 /** Template specialization. */
 /** Template specialization. */
 template<> void ScriptController::executeFunction<void>(const char* func, const char* args, ...)
 template<> void ScriptController::executeFunction<void>(const char* func, const char* args, ...)
 {
 {
+    int top = lua_gettop(_lua);
     va_list list;
     va_list list;
     va_start(list, args);
     va_start(list, args);
     executeFunctionHelper(0, func, args, &list);
     executeFunctionHelper(0, func, args, &list);
     va_end(list);
     va_end(list);
+    lua_settop(_lua, top);
 }
 }
 
 
 /** Template specialization. */
 /** Template specialization. */

Деякі файли не було показано, через те що забагато файлів було змінено