소스 검색

Merge pull request #1010 from blackberry/next

GamePlay v1.7.0 - merged next to master
Sean Paul Taylor 12 년 전
부모
커밋
bf18f79260
100개의 변경된 파일4121개의 추가작업 그리고 7445개의 파일을 삭제
  1. 211 207
      .gitignore
  2. 55 0
      CHANGES.md
  3. 5 8
      CMakeLists.txt
  4. 3 3
      README.md
  5. 3 0
      api/README.md
  6. 0 0
      api/header.html
  7. 0 0
      codestyle.xml
  8. 0 5
      gameplay-api/gameplay.html
  9. 0 150
      gameplay-encoder/CMakeLists.txt
  10. 0 94
      gameplay-encoder/README.md
  11. 0 101
      gameplay-encoder/src/DAEChannelTarget.cpp
  12. 0 84
      gameplay-encoder/src/DAEChannelTarget.h
  13. 0 78
      gameplay-encoder/src/DAEOptimizer.cpp
  14. 0 49
      gameplay-encoder/src/DAEOptimizer.h
  15. 0 2024
      gameplay-encoder/src/DAESceneEncoder.cpp
  16. 0 211
      gameplay-encoder/src/DAESceneEncoder.h
  17. 0 554
      gameplay-encoder/src/DAEUtil.cpp
  18. 0 203
      gameplay-encoder/src/DAEUtil.h
  19. 0 1837
      gameplay-encoder/src/FBXSceneEncoder.cpp
  20. 0 39
      gameplay-encoder/src/Material.cpp
  21. 0 37
      gameplay-encoder/src/Material.h
  22. 0 105
      gameplay-encoder/src/Model.cpp
  23. 0 183
      gameplay-template/res/box.dae
  24. BIN
      gameplay-template/res/box.gpb
  25. 2 2
      gameplay.doxyfile
  26. 38 38
      gameplay.sln
  27. 0 30
      gameplay.workspace
  28. 9 9
      gameplay.xcworkspace/contents.xcworkspacedata
  29. 0 248
      gameplay/.cproject
  30. 8 0
      gameplay/CMakeLists.txt
  31. 2 5
      gameplay/android/build.xml
  32. 6 14
      gameplay/android/jni/Android.mk
  33. 26 11
      gameplay/gameplay.vcxproj
  34. 70 28
      gameplay/gameplay.vcxproj.filters
  35. 431 426
      gameplay/gameplay.xcodeproj/project.pbxproj
  36. 7 0
      gameplay/gameplay.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  37. BIN
      gameplay/gameplay.xcodeproj/project.xcworkspace/xcuserdata/mozeal.xcuserdatad/UserInterfaceState.xcuserstate
  38. 1 1
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-ios.xcscheme
  39. 1 1
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-macosx.xcscheme
  40. 1 1
      gameplay/res/shaders/colored.frag
  41. 1 1
      gameplay/res/shaders/colored.vert
  42. 12 1
      gameplay/src/AIStateMachine.cpp
  43. 1 1
      gameplay/src/AbsoluteLayout.cpp
  44. 3 1
      gameplay/src/Animation.cpp
  45. 70 37
      gameplay/src/AnimationClip.cpp
  46. 18 1
      gameplay/src/AnimationClip.h
  47. 3 1
      gameplay/src/AnimationController.cpp
  48. 1 0
      gameplay/src/AudioBuffer.cpp
  49. 4 4
      gameplay/src/AudioSource.cpp
  50. 32 2
      gameplay/src/Bundle.cpp
  51. 8 0
      gameplay/src/Bundle.h
  52. 56 9
      gameplay/src/Button.cpp
  53. 15 0
      gameplay/src/Button.h
  54. 57 22
      gameplay/src/Camera.cpp
  55. 41 1
      gameplay/src/Camera.h
  56. 31 0
      gameplay/src/CheckBox.cpp
  57. 15 0
      gameplay/src/CheckBox.h
  58. 728 73
      gameplay/src/Container.cpp
  59. 111 19
      gameplay/src/Container.h
  60. 136 38
      gameplay/src/Control.cpp
  61. 60 17
      gameplay/src/Control.h
  62. 79 18
      gameplay/src/Curve.cpp
  63. 34 2
      gameplay/src/Curve.h
  64. 115 27
      gameplay/src/FileSystem.cpp
  65. 17 0
      gameplay/src/FileSystem.h
  66. 192 159
      gameplay/src/Form.cpp
  67. 17 1
      gameplay/src/Form.h
  68. 23 2
      gameplay/src/FrameBuffer.cpp
  69. 22 0
      gameplay/src/FrameBuffer.h
  70. 16 4
      gameplay/src/Frustum.cpp
  71. 21 1
      gameplay/src/Frustum.h
  72. 55 20
      gameplay/src/Game.cpp
  73. 63 6
      gameplay/src/Game.h
  74. 20 0
      gameplay/src/Game.inl
  75. 74 11
      gameplay/src/Gamepad.cpp
  76. 25 23
      gameplay/src/Gamepad.h
  77. 1 1
      gameplay/src/Image.h
  78. 148 0
      gameplay/src/ImageControl.cpp
  79. 146 0
      gameplay/src/ImageControl.h
  80. 89 3
      gameplay/src/Joint.cpp
  81. 26 7
      gameplay/src/Joint.h
  82. 8 11
      gameplay/src/Joystick.cpp
  83. 9 7
      gameplay/src/Label.cpp
  84. 85 0
      gameplay/src/Light.cpp
  85. 14 0
      gameplay/src/Light.h
  86. 3 3
      gameplay/src/Material.cpp
  87. 204 21
      gameplay/src/MaterialParameter.cpp
  88. 139 4
      gameplay/src/MaterialParameter.h
  89. 1 1
      gameplay/src/Matrix.h
  90. 66 0
      gameplay/src/MeshBatch.cpp
  91. 26 2
      gameplay/src/MeshBatch.h
  92. 2 57
      gameplay/src/MeshBatch.inl
  93. 2 2
      gameplay/src/MeshSkin.cpp
  94. 1 0
      gameplay/src/MeshSkin.h
  95. 10 7
      gameplay/src/Model.cpp
  96. 1 0
      gameplay/src/Model.h
  97. 65 23
      gameplay/src/Node.cpp
  98. 13 1
      gameplay/src/Node.h
  99. 3 3
      gameplay/src/ParticleEmitter.cpp
  100. 4 4
      gameplay/src/Pass.cpp

+ 211 - 207
.gitignore

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

+ 55 - 0
CHANGES.md

@@ -1,3 +1,58 @@
+## v1.7.0
+
+- Adds -m parameter to encoder to support exporting Material from FBX scenes.
+- Adds encoder animation grouping arguments for auto grouping and disabling grouping via -g:auto and -g:off
+- Adds UI ImageControl
+- Adds UI control events for enter and leave.
+- Adds UI and theme support for focus state on controls.
+- Adds Gamepad support in UI.
+- Adds smooth interial mouse scrolling.
+- Adds keyboard event handling code that was missed on some UI containers and controls.
+- Adds Game accelerometer and gyro improvements.
+- Adds XBox 360 controller support to MacOS X.
+- Adds RenderState enum for supporting cull side definition for front, back and front-back culling.
+- 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)
+- Adds gesture events to script.
+- Adds script friendly verions of Camera:project(..)
+- Adds support for defining lights in .scene file.
+- Adds MaterialParameter setter that are script friendly.
+- Adds methods to FrameBuffer for querying render target info.
+- Adds FileSystem::getDirectoryName()
+- Adds support so that Properties can be loaded relative to .property files.
+- Adds methods on ScriptController for registering and unregisering global script callback functions.
+- Adds Game::resizeEvent(..)
+- Adds Game::getArguments()
+- Adds Scene::load() so it can load from ".gpb" files.
+- Adds some additional operators overloads for Vector classes.
+- Adds RenderState::clearParameter method to allow existing material parameter values/bindings to be cleared.
+- Adds loopBlendTime property for animation clips to support interpolating between the end points of an animation clip when looping.
+- Fixes Xcode support to be up to date to version 6.1
+- Fixes Form::projectPoint for forms that are scaled.
+- Fixes Bullet NEON build problems.
+- Fixes problem with static rigid bodies attached to nodes.
+- Fixes problems with ghost objects and kinimatic rigid bodies.
+- Fixes userData pointer that was not cloned properly when cloning a node.
+- Fixes bug in createChannel with keyCount = 1.
+- Fixes encoder to ignore 1 frame animations caused by FBX export on Blender.
+- Fixes buggy tab order in UI.
+- Fixes touch event problems not propagating up on mobile touch platforms.
+- Fixes parent relationship on cloned Technique and Pass.
+- Fixes Node to include light contribution for bounding volume computations.
+- Fixed issue where multiple samplers pointing to the same texture would share the same state instead of using sampler-specific filter and wrap state.
+- Fixes calculation of the transform matrix in FBX encoder for lights properly considering vectors like pre and post rotation scenarios.
+- Fixes blurry borders on UI forms by using proper texture filtering.
+- Fixes lua dofile on Windows.
+- Fixes in colored shader when using spot ligts.
+- Fixes issue in encoder which was converting point lights into ambient lights when they had no decay.
+- Fixes to CMake projects on different platforms.
+- Cleans up Platform by moving commong platform code to Platform.cpp
+- Cleans up and changes Gamepad APIs.
+- Cleans repo directory restructure dropping gameplay- prefix on many folders and files.
+- Removes bin and external-deps folder/contents to external server with new install scripts that are now required to be run after cloning the repo.
+- Removes pre-compiled api/html docs to reduce repository bloat.
+- Removes support for COLLADA DAE and now support FBX.
+- Removes middle mouse button simulated desktop accelerometer.
+
 ## v1.6.0
 
 - Adds file Stream interface for reading/writing files instead of using fread/fwrite. 

+ 5 - 8
CMakeLists.txt

@@ -22,12 +22,9 @@ endif()
 add_subdirectory(gameplay)
 
 # gameplay samples
-add_subdirectory(gameplay-samples)
-
-# gameplay samples
-add_subdirectory(gameplay-tests)
-
-# gameplay encoder (See gameplay/bin)
-#add_subdirectory(gameplay-encoder)
-
+add_subdirectory(samples)
 
+# gameplay encoder
+# A pre-compiled executable can be found in 'gameplay/bin'
+# Uncomment out this line if you want to build the encoder instead of using the pre-compiled gameplay-encoder.
+#add_subdirectory(tools/encoder)

+ 3 - 3
README.md

@@ -1,4 +1,4 @@
-## gameplay v1.6.0
+## gameplay v1.7.0
 
 GamePlay3D is an open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
 
@@ -21,7 +21,7 @@ GamePlay3D is an open-source, cross-platform 3D native C++ game framework making
 - [Linux](https://github.com/blackberry/GamePlay/wiki/Linux-Setup) (using CMake)
 
 ## Roadmap for 'next' branch
-- [Version 1.7.0 Milestone](https://github.com/blackberry/GamePlay/issues?milestone=4)
+- [Version 2.0.0 Milestone](https://github.com/blackberry/GamePlay/issues?milestone=6)
 
 ## License
 The project is open sourced under the [Apache 2.0 license](http://www.tldrlegal.com/license/apache-license-2.0-%28apache-2.0%29).
@@ -31,7 +31,7 @@ Please log bugs under [Issues](https://github.com/blackberry/GamePlay/issues) on
 If you are unsure if your problem is a bug then post on the [Help Forum](http://www.gameplay3d.org/forums/viewforum.php?f=3).
 
 ## Feature Requests
-Please post feature requests on the [Feature Request Forum](http://www.gameplay3d.org/forums/viewforum.php?f=4). Approved feature requests will be added to the github issues list. 
+Please post feature requests on the [Feature Request Forum](http://www.gameplay3d.org/forums/viewforum.php?f=4). Approved feature requests will be added to the github backlog issues list. 
 
 ## Disclaimer
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 

+ 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 - 150
gameplay-encoder/CMakeLists.txt

@@ -1,150 +0,0 @@
-
-include_directories( 
-    ${CMAKE_SOURCE_DIR}/external-deps/zlib/include
-    ${CMAKE_SOURCE_DIR}/external-deps/libpng/include
-    ${CMAKE_SOURCE_DIR}/external-deps/freetype2/include
-    ${CMAKE_SOURCE_DIR}/external-deps/collada-dom/include
-    ${CMAKE_SOURCE_DIR}/external-deps/collada-dom/include/1.4
-    /usr/include/libxml2
-    /usr/include
-)
-
-add_definitions(-D_DEBUG)
-add_definitions(-D__linux__ -DNO_BOOST -DNO_ZAE)
-
-link_directories(
-    ${CMAKE_SOURCE_DIR}/external-deps/zlib/lib/linux/${ARCH_DIR}
-    ${CMAKE_SOURCE_DIR}/external-deps/libpng/lib/linux/${ARCH_DIR}
-    ${CMAKE_SOURCE_DIR}/external-deps/freetype2/lib/linux/${ARCH_DIR}
-    ${CMAKE_SOURCE_DIR}/external-deps/collada-dom/lib/linux/${ARCH_DIR}
-    /usr/lib
-)
-
-set(APP_LIBRARIES
-    collada14dom
-    pcre
-    pcrecpp
-    xml2
-    png
-    z   
-    freetype
-    pthread
-) 
-
-add_definitions(-lstdc++ -lcollada14dom -lpcre -lpcrecpp -lxml2 -lpng -lz -lfreetype -lpthread)
-
-set( APP_NAME gameplay-encoder )
-
-set(APP_SRC
-	src/AnimationChannel.cpp
-	src/AnimationChannel.h
-	src/Animation.cpp
-	src/Animation.h
-	src/Animations.cpp
-	src/Animations.h
-	src/Base.cpp
-	src/Base.h
-	src/BoundingVolume.cpp
-	src/BoundingVolume.h
-	src/Camera.cpp
-	src/Camera.h
-	src/Curve.cpp
-	src/Curve.h
-	src/Curve.inl
-	src/DAEChannelTarget.cpp
-	src/DAEChannelTarget.h
-	src/DAEOptimizer.cpp
-	src/DAEOptimizer.h
-	src/DAESceneEncoder.cpp
-	src/DAESceneEncoder.h
-	src/DAEUtil.cpp
-	src/DAEUtil.h
-	src/Effect.cpp
-	src/Effect.h
-	src/EncoderArguments.cpp
-	src/EncoderArguments.h
-	src/FBXSceneEncoder.cpp
-	src/FBXSceneEncoder.h
-	src/FileIO.cpp
-	src/FileIO.h
-	src/Font.cpp
-	src/Font.h
-	src/Glyph.cpp
-	src/Glyph.h
-	src/GPBDecoder.cpp
-	src/GPBDecoder.h
-	src/GPBFile.cpp
-	src/GPBFile.h
-	src/Heightmap.cpp
-	src/Heightmap.h
-	src/Image.cpp
-	src/Image.h
-	src/Light.cpp
-	src/Light.h
-	src/main.cpp
-	src/Material.cpp
-	src/Material.h
-	src/MaterialParameter.cpp
-	src/MaterialParameter.h
-	src/Matrix.cpp
-	src/Matrix.h
-	src/Mesh.cpp
-	src/Mesh.h
-	src/MeshPart.cpp
-	src/MeshPart.h
-	src/MeshSkin.cpp
-	src/MeshSkin.h
-	src/MeshSubSet.cpp
-	src/MeshSubSet.h
-	src/Model.cpp
-	src/Model.h
-	src/Node.cpp
-	src/Node.h
-	src/NormalMapGenerator.cpp
-	src/NormalMapGenerator.h
-	src/Object.cpp
-	src/Object.h
-	src/Quaternion.cpp
-	src/Quaternion.h
-	src/Quaternion.inl
-	src/Reference.cpp
-	src/Reference.h
-	src/ReferenceTable.cpp
-	src/ReferenceTable.h
-	src/Scene.cpp
-	src/Scene.h
-	src/StringUtil.cpp
-	src/StringUtil.h
-	src/Thread.h
-	src/Transform.cpp
-	src/Transform.h
-	src/TTFFontEncoder.cpp
-	src/TTFFontEncoder.h
-	src/Vector2.cpp
-	src/Vector2.h
-	src/Vector2.inl
-	src/Vector3.cpp
-	src/Vector3.h
-	src/Vector3.inl
-	src/Vector4.cpp
-	src/Vector4.h
-	src/Vector4.inl
-	src/Vertex.cpp
-	src/VertexElement.cpp
-	src/VertexElement.h
-	src/Vertex.h
-)
-
-add_executable(${APP_NAME}
-    ${APP_SRC}
-)
-
-target_link_libraries(${APP_NAME} ${APP_LIBRARIES})
-
-set_target_properties(${APP_NAME} PROPERTIES
-    OUTPUT_NAME "${APP_NAME}"
-    CLEAN_DIRECT_OUTPUT 1
-)
-
-source_group(src FILES ${APP_SRC})
-

+ 0 - 94
gameplay-encoder/README.md

@@ -1,94 +0,0 @@
-## gameplay-encoder
-Command-line tool for encoding games assets like true-type fonts and 3D scene files
-into a simple binary-based bundle file format for the gameplay 3D game framework runtime. 
-The 'bin' folder contains pre-built versions of the gameplay-encoder executables for 
-Windows 7, MacOS X and Linux Ubuntu 12 (32-bit) with support built-in support for:
-
-## TrueType Font
-TrueType Fonts represent a standard in defining outline fonts and has become the 
-most common format for fonts. The gameplay-encoder reads these fonts and binary encodes 
-them into a texture mapped base representation using a texture atlas and 8-bit alpha
-representation.
-
-## COLLADA Scene
-COLLADA is a royalty-free XML schema that enables digital asset exchange 
-within the interactive 3D industry. Most major 3D DCC tools support export to COLLADA 1.4.
-
-## FBX Scene
-Autodesk® FBX® asset exchange technology facilitates higher-fidelity data exchange 
-between several Autodesk content creation packages
-Autodesk® Maya®, Autodesk® 3ds Max®, Autodesk® MotionBuilder®, Autodesk® Mudbox®, and Autodesk® Softimage®
-For more information goto "http://www.autodesk.com/fbx".
-
-## Running gameplay-encoder
-Simply execute the gameplay-encoder command-line executable:
-
-`Usage: gameplay-encoder [options] <file(s)>`
-
-Note: On Linux Ubuntu 12 (64-bit), you must first install the required 32-bit libs via:
-
-`sudo apt-get install ia32-libs`
-
-## Building gameplay-encoder
-The gameplay-encoder comes pre-built for Windows 7, MacOS X and Linux Ubuntu 12 (32-bit) in the 'bin' folder.
-However, to build the gameplay-encoder yourself just open either the 
-Visual Studio 2010 project "gameplay-encoder.vccproj" on Windows 7 or
-XCode project "gameplay-encoder.xcodeproj" on MacOSX.
-Uncomment the root CMakeList.txt for the gameplay-encoder and run standard cmake .. from build then make.
-
-### Building with FBX Support on Windows 7 using Visual Studio 2010
-- Download and install the FBX SDK for Window VS2010. (http://www.autodesk.com/fbx)
-- Edit the project properties of "gameplay-encoder" for Debug
-- Add Preprocessor Definition "USE_FBX" (C++/Preprocessor)
-- Add the FBX SDK include directory to Additional Include Directories (C++/General)
-  * Example: C:/Program Files/Autodesk/FBX/FBX SDK/2013.3/include
-- Add the FBX lib directory to the Additional Library Directories (Linker/General)
-  * Example: C:/Program Files/Autodesk/FBX/FBX SDK/2013.3/lib/vs2010/x86
-- Add "fbxsdk-2013.3-mdd.lib"(Release) to the Additional Dependencies (Linker/Input)
-  * Example: fbxsdk-2013.3-mdd.lib
-- Build gameplay-encoder
-
-### Building with FBX Support on MacOS X using XCode 4
-- Download and install the FBX SDK for MacOS X (http://www.autodesk.com/fbx)
-- Edit the project properties of target "gameplay-encoder".
-- Add Preprocessor Macro "USE_FBX" to both Debug/Release sections. (Build Settings)
-- Add the FBX include directory to Header Search Paths: (Build Settings)
-  * Example: "/Applications/Autodesk/FBX SDK/2013.3/include" (Use quotes due to additional space in path)
-- Add the FBX library and dependency Library/Frameworks: (Build Phases -> Link Binary with Libraries)
-  * Example: /Applications/Autodesk/FBX SDK/2013.3/lib/gcc4/ub/libfbxsdk-2013.3-static.a  (Add Other)
-- Build gameplay-encoder
-
-### Building with FBX Support on Linux Ubuntu 12 (32-bit) using CMake
-- Download and install the FBX SDK for MacOS X (http://www.autodesk.com/fbx)
-- Edit the gameplay-encoder/CMakeLists.txt adding the following:
-- Add the FBX include directory to Header Search Paths: (Build Settings)
-  * Example: /usr/include/fbxsdk
-- Add Preprocessor Macro to the add"-DUSE_FBX" to the end of the add_definitions(...) section of the CMakeLists.txt
-- Add the FBX library path to the link_directories(...) section of the CMakeLists.txt
-  * Example: /usr/lib/gcc4
-- Add the FBX library to the set(APP_LIBRARIES {...} )
-  * Example: fbxsdk-2013.3-static
-- Add the FBX library to the library to the add_definitions(-l...) section of the CMakeLists.txt
-  * Example -lfbxsdk-2013.3-static
-- Build gameplay-encoder by uncommenting the last line in the gameplay/CMakeLists.txt and running the CMake build via:
-
-```
-mkdir build
-cd build
-cmake ..
-make
- ```
-
-## Bundle File Format
-The gameplay bundle file format is well defined in the gameplay-encoder/gameplay-bundle.txt file.
-
-## Bundle File Loading
-Bundle files can easily be loaded using the gameplay/Bundle.h which is part of the gameplay runtime framework.
-
-## Disclaimer
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
-PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
-OTHER DEALINGS IN THE SOFTWARE.

+ 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 - 1837
gameplay-encoder/src/FBXSceneEncoder.cpp

@@ -1,1837 +0,0 @@
-#ifdef USE_FBX
-
-#include <algorithm>
-#include <string>
-
-#include "FBXSceneEncoder.h"
-#include "EncoderArguments.h"
-
-using namespace gameplay;
-
-/**
- * Returns the aspect ratio from the given camera.
- * 
- * @param fbxCamera The FBX camera to get the aspect ratio from.
- * 
- * @return The aspect ratio from the camera.
- */
-static float getAspectRatio(FbxCamera* fbxCamera);
-
-/**
- * Returns the field of view Y from the given camera.
- * 
- * @param fbxCamera The camera to get the fiew of view from.
- * 
- * @return The field of view Y.
- */
-static float getFieldOfView(FbxCamera* fbxCamera);
-
-/**
- * Loads the texture coordinates from given mesh's polygon part into the vertex.
- * 
- * @param fbxMesh The mesh to get the polygon from.
- * @param uvs The UV list to load tex coords from.
- * @param uvSetIndex The UV set index of the uvs.
- * @param polyIndex The index of the polygon in the mesh.
- * @param posInPoly The position of the vertex in the polygon.
- * @param meshVertexIndex The index of the vertex in the mesh.
- * @param vertex The vertex to copy the texture coordinates to.
- */
-static void loadTextureCoords(FbxMesh* fbxMesh, const FbxGeometryElementUV* uvs, int uvSetIndex, int polyIndex, int posInPoly, int meshVertexIndex, Vertex* vertex);
-
-/**
- * Loads the normal from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to get the polygon from.
- * @param vertexIndex The vertex index in the mesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the tangent from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to load from.
- * @param vertexIndex The index of the vertex within fbxMesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the binormal from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to load from.
- * @param vertexIndex The index of the vertex within fbxMesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the vertex diffuse color from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to load from.
- * @param vertexIndex The index of the vertex within fbxMesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the blend weight and blend indices data into the vertex.
- * 
- * @param vertexWeights List of vertex weights. The x member contains the blendIndices. The y member contains the blendWeights.
- * @param vertex The vertex to copy the blend data to.
- */
-static void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex);
-
-/**
- * Loads the blend weights and blend indices from the given mesh.
- * 
- * Each element of weights is a list of Vector2s where "x" is the blend index and "y" is the blend weight.
- * 
- * @param fbxMesh The mesh to load from.
- * @param weights List of blend weights and blend indices for each vertex.
- * 
- * @return True if this mesh has a mesh skin, false otherwise.
- */
-static bool loadBlendWeights(FbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights);
-
-/**
- * Copies from an FBX matrix to a float[16] array.
- */
-static void copyMatrix(const FbxMatrix& fbxMatrix, float* matrix);
-
-/**
- * Copies from an FBX matrix to a gameplay matrix.
- */
-static void copyMatrix(const FbxMatrix& fbxMatrix, Matrix& matrix);
-
-/**
- * Finds the min and max start time and stop time of the given animation curve.
- * 
- * startTime is updated if the animation curve contains a start time that is less than startTime.
- * stopTime is updated if the animation curve contains a stop time that is greater than stopTime.
- * frameRate is updated if the animation curve contains a frame rate that is greater than frameRate.
- * 
- * @param animCurve The animation curve to read from.
- * @param startTime The min start time. (in/out)
- * @param stopTime The max stop time. (in/out)
- * @param frameRate The frame rate. (in/out)
- */
-static void findMinMaxTime(FbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate);
-
-/**
- * Appends key frame data to the given node for the specified animation target attribute.
- * 
- * @param fbxNode The node to get the matrix transform from.
- * @param channel The aniamtion channel to write values into.
- * @param time The time of the keyframe.
- * @param scale The evaluated scale for the keyframe.
- * @param rotation The evalulated rotation for the keyframe.
- * @param translation The evalulated translation for the keyframe.
-
- */
-static void appendKeyFrame(FbxNode* fbxNode, AnimationChannel* channel, float time, const Vector3& scale, const Quaternion& rotation, const Vector3& translation);
-
-/**
- * Decomposes the given node's matrix transform at the given time and copies to scale, rotation and translation.
- * 
- * @param fbxNode The node to get the matrix transform from.
- * @param time The time to get the matrix transform from.
- * @param scale The scale to copy to.
- * @param rotation The rotation to copy to.
- * @param translation The translation to copy to.
- */
-static void decompose(FbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation);
-
-/**
- * Creates an animation channel that targets the given node and target attribute using the given key times and key values.
- * 
- * @param fbxNode The node to target.
- * @param targetAttrib The attribute type to target.
- * @param keyTimes The key times for the animation channel.
- * @param keyValues The key values for the animation channel.
- * 
- * @return The newly created animation channel.
- */
-static AnimationChannel* createAnimationChannel(FbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues);
-
-void addScaleChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime);
-
-void addTranslateChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime);
-
-/**
- * Determines if it is possible to automatically group animations for mesh skins.
- * 
- * @param fbxScene The FBX scene to search.
- * 
- * @return True if there is at least one mesh skin that has animations that can be grouped.
- */
-bool isGroupAnimationPossible(FbxScene* fbxScene);
-bool isGroupAnimationPossible(FbxNode* fbxNode);
-bool isGroupAnimationPossible(FbxMesh* fbxMesh);
-
-FbxAnimCurve* getCurve(FbxPropertyT<FbxDouble3>& prop, FbxAnimLayer* animLayer, const char* pChannel)
-{
-#if FBXSDK_VERSION_MAJOR == 2013 && FBXSDK_VERSION_MINOR == 1
-    return prop.GetCurve<FbxAnimCurve>(animLayer, pChannel);
-#else
-    return prop.GetCurve(animLayer, pChannel);
-#endif
-}
-
-////////////////////////////////////
-// Member Functions
-////////////////////////////////////
-
-FBXSceneEncoder::FBXSceneEncoder()
-    : _groupAnimation(NULL), _autoGroupAnimations(false)
-{
-}
-
-FBXSceneEncoder::~FBXSceneEncoder()
-{
-}
-
-void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments& arguments)
-{
-    FbxManager* sdkManager = FbxManager::Create();
-    FbxIOSettings *ios = FbxIOSettings::Create(sdkManager, IOSROOT);
-    sdkManager->SetIOSettings(ios);
-    FbxImporter* importer = FbxImporter::Create(sdkManager,"");
-    
-    if (!importer->Initialize(filepath.c_str(), -1, sdkManager->GetIOSettings()))
-    {
-        LOG(1, "Call to FbxImporter::Initialize() failed.\n");
-        LOG(1, "Error returned: %s\n\n", importer->GetLastErrorString());
-        exit(-1);
-    }
-    
-    FbxScene* fbxScene = FbxScene::Create(sdkManager,"__FBX_SCENE__");
-
-    print("Loading FBX file.");
-    importer->Import(fbxScene);
-    importer->Destroy();
-
-    // Determine if animations should be grouped.
-    if (arguments.getGroupAnimationAnimationId().empty() && isGroupAnimationPossible(fbxScene))
-    {
-        if (promptUserGroupAnimations())
-        {
-            _autoGroupAnimations = true;
-        }
-    }
-
-    print("Loading Scene.");
-    loadScene(fbxScene);
-    print("Loading animations.");
-    loadAnimations(fbxScene, arguments);
-    sdkManager->Destroy();
-
-    print("Optimizing GamePlay Binary.");
-    _gamePlayFile.adjust();
-    if (_autoGroupAnimations)
-    {
-        _gamePlayFile.groupMeshSkinAnimations();
-    }
-    
-    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());
-        if (!_gamePlayFile.saveBinary(outputFilePath))
-        {
-            LOG(1, "Error writing binary file: %s\n", outputFilePath.c_str());
-        }
-    }
-}
-
-void FBXSceneEncoder::loadScene(FbxScene* fbxScene)
-{
-    Scene* scene = new Scene();
-    scene->setId(fbxScene->GetName());
-    if (scene->getId().length() == 0)
-    {
-        scene->setId("__SCENE__");
-    }
-
-    // Load all of the nodes and their contents.
-    FbxNode* rootNode = fbxScene->GetRootNode();
-    if (rootNode)
-    {
-        print("Triangulate.");
-        triangulateRecursive(rootNode);
-
-        print("Load nodes.");
-        // Don't include the FBX root node in the GPB.
-        const int childCount = rootNode->GetChildCount();
-        for (int i = 0; i < childCount; ++i)
-        {
-            Node* node = loadNode(rootNode->GetChild(i));
-            if (node)
-            {
-                scene->add(node);
-            }
-        }
-    }
-
-    // Load the MeshSkin information from the scene's poses.
-    loadBindShapes(fbxScene);
-
-    // Find the ambient light of the scene
-    FbxColor ambientColor = fbxScene->GetGlobalSettings().GetAmbientColor();
-    scene->setAmbientColor((float)ambientColor.mRed, (float)ambientColor.mGreen, (float)ambientColor.mBlue);
-    
-    // Assign the first camera node (if there is one) in the scene as the active camera
-    // This ensures that if there's a camera in the scene that it is assigned as the 
-    // active camera.
-    // TODO: add logic to find the "active" camera node in the fbxScene
-    scene->setActiveCameraNode(scene->getFirstCameraNode());
-    
-    _gamePlayFile.addScene(scene);
-}
-
-void FBXSceneEncoder::loadAnimationChannels(FbxAnimLayer* animLayer, FbxNode* fbxNode, Animation* animation)
-{
-    const char* name = fbxNode->GetName();
-    //Node* node = _gamePlayFile.getNode(name);
-
-    // Determine which properties are animated on this node
-    // Find the transform at each key frame
-    // TODO: Ignore properties that are not animated (scale, rotation, translation)
-    // This should result in only one animation channel per animated node.
-
-    float startTime = FLT_MAX, stopTime = -1.0f, frameRate = -FLT_MAX;
-    bool tx = false, ty = false, tz = false, rx = false, ry = false, rz = false, sx = false, sy = false, sz = false;
-    FbxAnimCurve* animCurve = NULL;
-    animCurve = getCurve(fbxNode->LclTranslation, animLayer, FBXSDK_CURVENODE_COMPONENT_X);
-    if (animCurve)
-    {
-        tx = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclTranslation, animLayer, FBXSDK_CURVENODE_COMPONENT_Y);
-    if (animCurve)
-    {
-        ty = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclTranslation, animLayer, FBXSDK_CURVENODE_COMPONENT_Z);
-    if (animCurve)
-    {
-        tz = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclRotation, animLayer, FBXSDK_CURVENODE_COMPONENT_X);
-    if (animCurve)
-    {
-        rx = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclRotation, animLayer, FBXSDK_CURVENODE_COMPONENT_Y);
-    if (animCurve)
-    {
-        ry = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclRotation, animLayer, FBXSDK_CURVENODE_COMPONENT_Z);
-    if (animCurve)
-    {
-        rz = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclScaling, animLayer, FBXSDK_CURVENODE_COMPONENT_X);
-    if (animCurve)
-    {
-        sx = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclScaling, animLayer, FBXSDK_CURVENODE_COMPONENT_Y);
-    if (animCurve)
-    {
-        sy = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-    animCurve = getCurve(fbxNode->LclScaling, animLayer, FBXSDK_CURVENODE_COMPONENT_Z);
-    if (animCurve)
-    {
-        sz = true;
-        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
-    }
-
-    if (!(sx || sy || sz || rx || ry || rz || tx || ty || tz))
-        return; // no animation channels
-
-    assert(startTime != FLT_MAX);
-    assert(stopTime >= 0.0f);
-
-    // Determine which animation channels to create
-    std::vector<unsigned int> channelAttribs;
-    if (sx && sy && sz)
-    {
-        if (rx || ry || rz)
-        {
-            if (tx && ty && tz)
-            {
-                channelAttribs.push_back(Transform::ANIMATE_SCALE_ROTATE_TRANSLATE);
-            }
-            else
-            {
-                channelAttribs.push_back(Transform::ANIMATE_SCALE_ROTATE);
-                if (tx)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X);
-                if (ty)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y);
-                if (tz)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z);
-            }
-        }
-        else
-        {
-            if (tx && ty && tz)
-            {
-                channelAttribs.push_back(Transform::ANIMATE_SCALE_TRANSLATE);
-            }
-            else
-            {
-                channelAttribs.push_back(Transform::ANIMATE_SCALE);
-                if (tx)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X);
-                if (ty)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y);
-                if (tz)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z);
-            }
-        }
-    }
-    else
-    {
-        if (rx || ry || rz)
-        {
-            if (tx && ty && tz)
-            {
-                channelAttribs.push_back(Transform::ANIMATE_ROTATE_TRANSLATE);
-            }
-            else
-            {
-                channelAttribs.push_back(Transform::ANIMATE_ROTATE);
-                if (tx)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X);
-                if (ty)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y);
-                if (tz)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z);
-            }
-        }
-        else
-        {
-            if (tx && ty && tz)
-            {
-                channelAttribs.push_back(Transform::ANIMATE_TRANSLATE);
-            }
-            else
-            {
-                if (tx)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_X);
-                if (ty)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Y);
-                if (tz)
-                    channelAttribs.push_back(Transform::ANIMATE_TRANSLATE_Z);
-            }
-        }
-
-        if (sx)
-            channelAttribs.push_back(Transform::ANIMATE_SCALE_X);
-        if (sy)
-            channelAttribs.push_back(Transform::ANIMATE_SCALE_Y);
-        if (sz)
-            channelAttribs.push_back(Transform::ANIMATE_SCALE_Z);
-    }
-    unsigned int channelCount = channelAttribs.size();
-    assert(channelCount > 0);
-
-    // Allocate channel list
-    int channelStart = animation->getAnimationChannelCount();
-    for (unsigned int i = 0; i < channelCount; ++i)
-    {
-        AnimationChannel* channel = new AnimationChannel();
-        channel->setTargetId(name);
-        channel->setInterpolation(AnimationChannel::LINEAR);
-        channel->setTargetAttribute(channelAttribs[i]);
-        animation->add(channel);
-    }
-
-    // Evaulate animation curve in increments of frameRate and populate channel data.
-    FbxAMatrix fbxMatrix;
-    Matrix matrix;
-    float increment = 1000.0f / frameRate;
-    for (float time = startTime; time <= stopTime; time += increment)
-    {
-        // Clamp time to stopTime
-        time = std::min(time, stopTime);
-
-        // Evalulate the animation at this time
-        FbxTime kTime;
-        kTime.SetMilliSeconds((FbxLongLong)time);
-        fbxMatrix = fbxNode->EvaluateLocalTransform(kTime);
-        copyMatrix(fbxMatrix, matrix);
-
-        // Decompose the evalulated transformation matrix into separate
-        // scale, rotation and translation.
-        Vector3 scale;
-        Quaternion rotation;
-        Vector3 translation;
-        matrix.decompose(&scale, &rotation, &translation);
-        rotation.normalize();
-
-        // Append keyframe data to all channels
-        for (unsigned int i = channelStart, channelEnd = channelStart + channelCount; i < channelEnd; ++i)
-        {
-            appendKeyFrame(fbxNode, animation->getAnimationChannel(i), time, scale, rotation, translation);
-        }
-    }
-
-    if (_groupAnimation != animation)
-    {
-        // TODO explain
-        _gamePlayFile.addAnimation(animation);
-    }
-}
-
-void FBXSceneEncoder::loadAnimationLayer(FbxAnimLayer* fbxAnimLayer, FbxNode* fbxNode, const EncoderArguments& arguments)
-{
-    bool animationGroupId = false;
-    const char* name = fbxNode->GetName();
-    // Check if this node's animations are supposed to be grouped
-    if (name && arguments.containsGroupNodeId(name))
-    {
-        animationGroupId = true;
-        _groupAnimation = new Animation();
-        _groupAnimation->setId(arguments.getAnimationId(name));
-    }
-    Animation* animation = _groupAnimation;
-    if (!animation)
-    {
-        animation = new Animation();
-        animation->setId(name);
-    }
-    loadAnimationChannels(fbxAnimLayer, fbxNode, animation);
-
-    const int childCount = fbxNode->GetChildCount();
-    for (int modelCount = 0; modelCount < childCount; ++modelCount)
-    {
-        loadAnimationLayer(fbxAnimLayer, fbxNode->GetChild(modelCount), arguments);
-    }
-    if (animationGroupId)
-    {
-        _gamePlayFile.addAnimation(_groupAnimation);
-        _groupAnimation = NULL;
-    }
-}
-
-void FBXSceneEncoder::loadAnimations(FbxScene* fbxScene, const EncoderArguments& arguments)
-{
-    FbxAnimEvaluator* evaluator = fbxScene->GetEvaluator();
-    if (!evaluator)
-        return;
-    FbxAnimStack* animStack = evaluator->GetContext();
-    if (!animStack)
-        return;
-
-    for (int i = 0; i < fbxScene->GetSrcObjectCount(FBX_TYPE(FbxAnimStack)); ++i)
-    {
-        FbxAnimStack* animStack = FbxCast<FbxAnimStack>(fbxScene->GetSrcObject(FBX_TYPE(FbxAnimStack), i));
-        int nbAnimLayers = animStack->GetMemberCount(FBX_TYPE(FbxAnimLayer));
-        for (int l = 0; l < nbAnimLayers; ++l)
-        {
-            FbxAnimLayer* animLayer = animStack->GetMember(FBX_TYPE(FbxAnimLayer), l);
-            loadAnimationLayer(animLayer, fbxScene->GetRootNode(), arguments);
-        }
-    }
-}
-
-Node* FBXSceneEncoder::loadNode(FbxNode* fbxNode)
-{
-    Node* node = NULL;
-
-    // Check if this node has already been loaded
-    const char* id = fbxNode->GetName();
-    if (id && strlen(id) > 0)
-    {
-        node = _gamePlayFile.getNode(fbxNode->GetName());
-        if (node)
-        {
-            return node;
-        }
-    }
-    node = new Node();
-    if (id)
-    {
-        node->setId(id);
-    }
-    _gamePlayFile.addNode(node);
-
-    transformNode(fbxNode, node);
-    
-    loadCamera(fbxNode, node);
-    loadLight(fbxNode, node);
-    loadModel(fbxNode, node);
-
-    if (fbxNode->GetSkeleton())
-    {
-        // Indicate that this is a joint node for the purpose of debugging.
-        // The XML debug output will print that this node is a joint.
-        node->setIsJoint(true);
-    }
-
-    // Load child nodes
-    const int childCount = fbxNode->GetChildCount();
-    for (int i = 0; i < childCount; ++i)
-    {
-        Node* child = loadNode(fbxNode->GetChild(i));
-        if (child)
-        {
-            node->addChild(child);
-        }
-    }
-    return node;
-}
-
-Mesh* FBXSceneEncoder::getMesh(FbxUInt64 meshId)
-{
-    // Check if this mesh was already loaded.
-    std::map<FbxUInt64, Mesh*>::iterator it = _meshes.find(meshId);
-    if (it != _meshes.end())
-    {
-        return it->second;
-    }
-    return NULL;
-}
-
-void FBXSceneEncoder::saveMesh(FbxUInt64 meshId, Mesh* mesh)
-{
-    assert(mesh);
-    if (!getMesh(meshId))
-    {
-        _meshes[meshId] = mesh;
-    }
-}
-
-void FBXSceneEncoder::print(const char* str)
-{
-    LOG(1, "%s\n", str);
-}
-
-void FBXSceneEncoder::transformNode(FbxNode* fbxNode, Node* node)
-{
-    FbxAMatrix matrix;
-    float m[16];
-    
-    if (fbxNode->GetCamera() || fbxNode->GetLight())
-    {
-        FbxAMatrix rotateAdjust;
-        
-        if(fbxNode->GetLight())
-        {
-            /*
-             * according to the fbx-documentation the light's forward vector
-             * points along a node's negative Y axis.
-             * so we have to rotate it by 90° around the X-axis to correct it.
-             */
-            if(fbxNode->RotationActive.Get())
-            {
-                const FbxVector4& postRotation = fbxNode->PostRotation.Get();
-                fbxNode->SetPostRotation(FbxNode::eSourcePivot, FbxVector4(postRotation.mData[0] + 90.0,
-                                                                           postRotation.mData[1],
-                                                                           postRotation.mData[2])
-                                         );
-            }
-            else
-            {
-                // if the rotation is deactivated we have to rotate it anyway to get the correct transformation in the end
-                rotateAdjust.SetR(FbxVector4(-90.0, 0.0, 0.0));
-            }
-            
-            matrix = fbxNode->EvaluateLocalTransform() * rotateAdjust;
-        }
-        else if(fbxNode->GetCamera())
-        {
-            // TODO: use the EvaluateLocalTransform() function for the transformations for the camera
-            /*
-             * the current implementation ignores pre- and postrotation among others (usually happens with fbx-export from blender)
-             *
-             * Some info for a future implementation:
-             * according to the fbx-documentation the camera's forward vector
-             * points along a node's positive X axis.
-             * so we have to correct it if we use the EvaluateLocalTransform-function
-             * just rotating it by 90° around the Y axis (similar to above) doesn't work
-             */
-            matrix.SetTRS(fbxNode->LclTranslation.Get(), fbxNode->LclRotation.Get(), fbxNode->LclScaling.Get());
-        }
-        
-        copyMatrix(matrix, m);
-        node->setTransformMatrix(m);
-    }
-    else
-    {
-        matrix = fbxNode->EvaluateLocalTransform();
-        copyMatrix(matrix, m);
-        node->setTransformMatrix(m);
-    }
-}
-
-void FBXSceneEncoder::loadBindShapes(FbxScene* fbxScene)
-{
-    float m[16];
-    const int poseCount = fbxScene->GetPoseCount();
-    for (int i = 0; i < poseCount; ++i)
-    {
-        FbxPose* pose = fbxScene->GetPose(i);
-        assert(pose);
-        if (pose->IsBindPose() && pose->GetCount() > 0)
-        {
-            FbxNode* fbxNode = pose->GetNode(0);
-            if (fbxNode->GetMesh() != NULL)
-            {
-                Node* node = _gamePlayFile.getNode(fbxNode->GetName());
-                assert(node && node->getModel());
-
-                Model* model = node->getModel();
-                if (model && model->getSkin())
-                {
-                    MeshSkin* skin = model->getSkin();
-                    copyMatrix(pose->GetMatrix(0), m);
-                    skin->setBindShape(m);
-                }
-            }
-        }
-    }
-}
-
-void FBXSceneEncoder::loadCamera(FbxNode* fbxNode, Node* node)
-{
-    FbxCamera* fbxCamera = fbxNode->GetCamera();
-    if (!fbxCamera)
-    {
-        return;
-    }
-    Camera* camera = new Camera();
-    const char* name = fbxNode->GetName();
-    if (name)
-    {
-        std::string id(name);
-        id.append("_Camera");
-        camera->setId(id);
-    }
-    camera->setAspectRatio(getAspectRatio(fbxCamera));
-    camera->setNearPlane((float)fbxCamera->NearPlane.Get());
-    camera->setFarPlane((float)fbxCamera->FarPlane.Get());
-
-    if (fbxCamera->ProjectionType.Get() == FbxCamera::eOrthogonal)
-    {
-        camera->setOrthographic();
-        camera->setViewportWidth((float)fbxCamera->GetApertureWidth());
-        camera->setViewportWidth((float)fbxCamera->GetApertureHeight());
-        // xmag in FBX can be calculated from: OrthoZoom * 30.0 / 2.0
-        camera->setViewportWidth((float)fbxCamera->OrthoZoom.Get() * 15.0f);
-    }
-    else if (fbxCamera->ProjectionType.Get() == FbxCamera::ePerspective)
-    {
-        camera->setPerspective();
-        camera->setFieldOfView(getFieldOfView(fbxCamera));
-    }
-    else
-    {
-        LOG(2, "Warning: Unknown camera type in node.\n");
-        return;
-    }
-    _gamePlayFile.addCamera(camera);
-    node->setCamera(camera);
-}
-
-void FBXSceneEncoder::loadLight(FbxNode* fbxNode, Node* node)
-{
-    FbxLight* fbxLight = fbxNode->GetLight();
-    if (!fbxLight)
-    {
-        return;
-    }
-    Light* light = new Light();
-    const char* name = fbxNode->GetName();
-    if (name)
-    {
-        std::string id(name);
-        id.append("_Light");
-        light->setId(id);
-    }
-
-    FbxDouble3 color = fbxLight->Color.Get();
-    light->setColor((float)color[0], (float)color[1], (float)color[2]);
-    
-    switch (fbxLight->LightType.Get())
-    {
-    case FbxLight::ePoint:
-    {
-        FbxLight::EDecayType decayType = fbxLight->DecayType.Get();
-        switch (decayType)
-        {
-        case FbxLight::eNone:
-            // No decay. Can assume we have an ambient light, because ambient lights in the scene are 
-            // converted to point lights with no decay when exporting to FBX.
-            light->setAmbientLight();
-            break;
-        case FbxLight::eLinear:
-            light->setPointLight();
-            light->setLinearAttenuation((float)fbxLight->DecayStart.Get());
-            break;
-        case FbxLight::eQuadratic:
-            light->setPointLight();
-            light->setQuadraticAttenuation((float)fbxLight->DecayStart.Get());
-            break;
-        case FbxLight::eCubic:
-        default:
-            // Not supported..
-            break;
-        }
-        break;
-    }
-    case FbxLight::eDirectional:
-    {
-        light->setDirectionalLight();
-        break;
-    }
-    case FbxLight::eSpot:
-    {
-        light->setSpotLight();
-
-        FbxLight::EDecayType decayType = fbxLight->DecayType.Get();
-        switch (decayType)
-        {
-        case FbxLight::eNone:
-            // No decay.
-            break;
-        case FbxLight::eLinear:
-            light->setLinearAttenuation((float)fbxLight->DecayStart.Get());
-            break;  
-        case FbxLight::eQuadratic:
-            light->setQuadraticAttenuation((float)fbxLight->DecayStart.Get());
-            break;
-        case FbxLight::eCubic:
-            // Not supported..
-            break;
-        }
-
-        light->setFalloffAngle(MATH_DEG_TO_RAD((float)fbxLight->OuterAngle.Get())); // fall off angle
-        break;
-    }
-    default:
-    {
-        LOG(2, "Warning: Unknown light type in node.\n");
-        return;
-    }
-    }
-
-    _gamePlayFile.addLight(light);
-    node->setLight(light);
-}
-
-void FBXSceneEncoder::loadModel(FbxNode* fbxNode, Node* node)
-{
-    FbxMesh* fbxMesh = fbxNode->GetMesh();
-    if (!fbxMesh)
-    {
-        return;
-    }
-    if (fbxMesh->IsTriangleMesh())
-    {
-        Mesh* mesh = loadMesh(fbxMesh);
-        Model* model = new Model();
-        model->setMesh(mesh);
-        node->setModel(model);
-        loadSkin(fbxMesh, model);
-        if (model->getSkin())
-        {
-            // TODO: explain
-            node->resetTransformMatrix();
-        }
-    }
-}
-
-void FBXSceneEncoder::loadSkin(FbxMesh* fbxMesh, Model* model)
-{
-    const int deformerCount = fbxMesh->GetDeformerCount();
-    for (int i = 0; i < deformerCount; ++i)
-    {
-        FbxDeformer* deformer = fbxMesh->GetDeformer(i);
-        if (deformer->GetDeformerType() == FbxDeformer::eSkin)
-        {
-            FbxSkin* fbxSkin = static_cast<FbxSkin*>(deformer);
-
-            MeshSkin* skin = new MeshSkin();
-
-            std::vector<std::string> jointNames;
-            std::vector<Node*> joints;
-            std::vector<Matrix> bindPoses;
-
-            const int clusterCount = fbxSkin->GetClusterCount();
-            for (int j = 0; j < clusterCount; ++j)
-            {
-                FbxCluster* cluster = fbxSkin->GetCluster(j);
-                assert(cluster);
-                FbxNode* linkedNode = cluster->GetLink();
-                if (linkedNode && linkedNode->GetSkeleton())
-                {
-                    const char* jointName = linkedNode->GetName();
-                    assert(jointName);
-                    jointNames.push_back(jointName);
-                    Node* joint = loadNode(linkedNode);
-                    assert(joint);
-                    joints.push_back(joint);
-
-                    FbxAMatrix matrix;
-                    cluster->GetTransformLinkMatrix(matrix);
-                    Matrix m;
-                    copyMatrix(matrix.Inverse(), m);
-                    bindPoses.push_back(m);
-                }
-            }
-            skin->setJointNames(jointNames);
-            skin->setJoints(joints);
-            skin->setBindPoses(bindPoses);
-            model->setSkin(skin);
-            break;
-        }
-    }
-}
-
-Mesh* FBXSceneEncoder::loadMesh(FbxMesh* fbxMesh)
-{
-    // Check if this mesh has already been loaded.
-    Mesh* mesh = getMesh(fbxMesh->GetUniqueID());
-    if (mesh)
-    {
-        return mesh;
-    }
-    mesh = new Mesh();
-    // GamePlay requires that a mesh have a unique ID but FbxMesh doesn't have a string ID.
-    const char* name = fbxMesh->GetNode()->GetName();
-    if (name)
-    {
-        std::string id(name);
-        id.append("_Mesh");
-        mesh->setId(id);
-    }
-
-    // The number of mesh parts is equal to the number of materials that affect this mesh.
-    // There is always at least one mesh part.
-    std::vector<MeshPart*> meshParts;
-    const int materialCount = fbxMesh->GetNode()->GetMaterialCount();
-    int meshPartSize = (materialCount > 0) ? materialCount : 1;
-    for (int i = 0; i < meshPartSize; ++i)
-    {
-        meshParts.push_back(new MeshPart());
-    }
-
-    // Find the blend weights and blend indices if this mesh is skinned.
-    std::vector<std::vector<Vector2> > weights;
-    bool hasSkin = loadBlendWeights(fbxMesh, weights);
-    
-    // Get list of uv sets for mesh
-    FbxStringList uvSetNameList;
-    fbxMesh->GetUVSetNames(uvSetNameList);
-    const int uvSetCount = uvSetNameList.GetCount();
-
-    int vertexIndex = 0;
-    FbxVector4* controlPoints = fbxMesh->GetControlPoints();
-    const int polygonCount = fbxMesh->GetPolygonCount();
-    for (int polyIndex = 0; polyIndex < polygonCount; ++polyIndex)
-    {
-        const int polygonSize = fbxMesh->GetPolygonSize(polyIndex);
-        for (int posInPoly = 0; posInPoly < polygonSize; ++posInPoly)
-        {
-            int controlPointIndex = fbxMesh->GetPolygonVertex(polyIndex, posInPoly);
-            Vertex vertex;
-
-            FbxVector4& position = controlPoints[controlPointIndex];
-            vertex.position.x = (float)position[0];
-            vertex.position.y = (float)position[1];
-            vertex.position.z = (float)position[2];
-
-            // Load tex coords for all uv sets
-            for (int uvSetIndex = 0; uvSetIndex < uvSetCount; ++uvSetIndex)
-            {
-                const FbxGeometryElementUV* uvElement = fbxMesh->GetElementUV(uvSetNameList.GetStringAt(uvSetIndex));
-                if (uvElement)
-                    loadTextureCoords(fbxMesh, uvElement, uvSetIndex, polyIndex, posInPoly, vertexIndex, &vertex);
-            }
-
-            // Load other data
-            loadNormal(fbxMesh, vertexIndex, controlPointIndex, &vertex);
-            loadTangent(fbxMesh, vertexIndex, controlPointIndex, &vertex);
-            loadBinormal(fbxMesh, vertexIndex, controlPointIndex, &vertex);
-            loadVertexColor(fbxMesh, vertexIndex, controlPointIndex, &vertex);
-
-            if (hasSkin)
-            {
-                loadBlendData(weights[controlPointIndex], &vertex);
-            }
-
-            // Determine which mesh part this vertex index should be added to based on the material that affects it.
-            int meshPartIndex = 0;
-            const int elementMatrialCount = fbxMesh->GetElementMaterialCount();
-            for (int k = 0; k < elementMatrialCount; ++k)
-            {
-                FbxGeometryElementMaterial* elementMaterial = fbxMesh->GetElementMaterial(k);
-                meshPartIndex = elementMaterial->GetIndexArray().GetAt(polyIndex);
-            }
-
-            // Add the vertex to the mesh if it hasn't already been added and find the vertex index.
-            unsigned int index;
-            if (mesh->contains(vertex))
-            {
-                index = mesh->getVertexIndex(vertex);
-            }
-            else
-            {
-                index = mesh->addVertex(vertex);
-            }
-            meshParts[meshPartIndex]->addIndex(index);
-            vertexIndex++;
-        }
-    }
-
-    const size_t meshpartsSize = meshParts.size();
-    for (size_t i = 0; i < meshpartsSize; ++i)
-    {
-        mesh->addMeshPart(meshParts[i]);
-    }
-
-    // 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);
-
-    const Vertex& vertex = mesh->vertices[0];
-    // Normals
-    if (vertex.hasNormal)
-    {
-        mesh->addVetexAttribute(NORMAL, Vertex::NORMAL_COUNT);
-    }
-    // Tangents
-    if (vertex.hasTangent)
-    {
-        mesh->addVetexAttribute(TANGENT, Vertex::TANGENT_COUNT);
-    }
-    // Binormals
-    if (vertex.hasBinormal)
-    {
-        mesh->addVetexAttribute(BINORMAL, Vertex::BINORMAL_COUNT);
-    }
-    // Texture Coordinates
-    for (unsigned int i = 0; i < MAX_UV_SETS; ++i)
-    {
-        if (vertex.hasTexCoord[i])
-        {
-            mesh->addVetexAttribute(TEXCOORD0 + i, Vertex::TEXCOORD_COUNT);
-        }
-    }
-    // Diffuse Color
-    if (vertex.hasDiffuse)
-    {
-        mesh->addVetexAttribute(COLOR, Vertex::DIFFUSE_COUNT);
-    }
-    // Skinning BlendWeights BlendIndices
-    if (vertex.hasWeights)
-    {
-        mesh->addVetexAttribute(BLENDWEIGHTS, Vertex::BLEND_WEIGHTS_COUNT);
-        mesh->addVetexAttribute(BLENDINDICES, Vertex::BLEND_INDICES_COUNT);
-    }
-
-    _gamePlayFile.addMesh(mesh);
-    saveMesh(fbxMesh->GetUniqueID(), mesh);
-    return mesh;
-}
-
-void FBXSceneEncoder::triangulateRecursive(FbxNode* fbxNode)
-{
-    // Triangulate all NURBS, patch and mesh under this node recursively.
-    FbxNodeAttribute* nodeAttribute = fbxNode->GetNodeAttribute();
-
-    if (nodeAttribute)
-    {
-        if (nodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh ||
-            nodeAttribute->GetAttributeType() == FbxNodeAttribute::eNurbs ||
-            nodeAttribute->GetAttributeType() == FbxNodeAttribute::eNurbsSurface ||
-            nodeAttribute->GetAttributeType() == FbxNodeAttribute::ePatch)
-        {
-            FbxGeometryConverter converter(fbxNode->GetFbxManager());
-            converter.TriangulateInPlace(fbxNode);
-        }
-    }
-
-    const int childCount = fbxNode->GetChildCount();
-    for (int childIndex = 0; childIndex < childCount; ++childIndex)
-    {
-        triangulateRecursive(fbxNode->GetChild(childIndex));
-    }
-}
-
-////////////////////////////////////
-// Functions
-////////////////////////////////////
-
-float getAspectRatio(FbxCamera* fbxCamera)
-{
-    return (float)fbxCamera->FilmAspectRatio.Get();
-    /*
-    FbxCamera::ECameraAspectRatioMode camAspectRatioMode = fbxCamera->GetAspectRatioMode();
-    double aspectX = fbxCamera->AspectWidth.Get();
-    double aspectY = fbxCamera->AspectHeight.Get();
-    double aspectRatio = 1.333333;
-    switch ( camAspectRatioMode)
-    {
-    case FbxCamera::eWINDOW_SIZE:
-        aspectRatio = aspectX / aspectY;
-        break;
-    case FbxCamera::eFIXED_RATIO:
-        aspectRatio = aspectX;
-        break;
-    case FbxCamera::eFIXED_RESOLUTION:
-        aspectRatio = aspectX / aspectY * fbxCamera->GetPixelRatio();
-        break;
-    case FbxCamera::eFIXED_WIDTH:
-        aspectRatio = fbxCamera->GetPixelRatio() / aspectY;
-        break;
-    case FbxCamera::eFIXED_HEIGHT:
-        aspectRatio = fbxCamera->GetPixelRatio() * aspectX;
-        break;
-    default:
-        break;
-    }
-    return (float)aspectRatio;
-    */
-}
-
-inline double vfov(double hfov, double aspect)
-{
-    static const double MATH_PI_180 = 0.01745329251994329576923690768489;
-    static const double MATH_180_PI = 57.295779513082320876798154814105;
-    return (2.0 * atan((aspect) * tan( (hfov * MATH_PI_180) * 0.5)) * MATH_180_PI);
-}
-
-float getFieldOfView(FbxCamera* fbxCamera)
-{
-    double fieldOfViewX = 0.0;
-    double fieldOfViewY = 0.0;
-    double filmHeight = fbxCamera->GetApertureHeight();
-    double filmWidth = fbxCamera->GetApertureWidth() * fbxCamera->GetSqueezeRatio();
-    double apertureRatio = filmHeight / filmWidth;
-    if ( fbxCamera->GetApertureMode() == FbxCamera::eVertical)
-    {
-        fieldOfViewY = fbxCamera->FieldOfView.Get();
-    }
-    else if (fbxCamera->GetApertureMode() == FbxCamera::eHorizontal)
-    {
-        fieldOfViewX = fbxCamera->FieldOfView.Get();
-        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
-    }
-    else if (fbxCamera->GetApertureMode() == FbxCamera::eFocalLength)
-    {
-        fieldOfViewX = fbxCamera->ComputeFieldOfView(fbxCamera->FocalLength.Get());
-        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
-    }
-    else if (fbxCamera->GetApertureMode() == FbxCamera::eHorizAndVert)
-    {
-        fieldOfViewY = fbxCamera->FieldOfViewY.Get();
-    }
-    else
-    {
-        fieldOfViewY = 45.0;
-    }
-    return (float)fieldOfViewY;
-}
-
-void loadTextureCoords(FbxMesh* fbxMesh, const FbxGeometryElementUV* uvs, int uvSetIndex, int polyIndex, int posInPoly, int meshVertexIndex, Vertex* vertex)
-{
-    assert(fbxMesh && polyIndex >=0 && posInPoly >= 0);
-
-    const bool useIndex = uvs->GetReferenceMode() != FbxGeometryElement::eDirect;
-    const int indexCount = useIndex ? uvs->GetIndexArray().GetCount() : 0;
-    int uvIndex = -1;
-
-    switch (uvs->GetMappingMode())
-    {
-    case FbxGeometryElement::eByControlPoint:
-        {
-            // Get the index of the current vertex in control points array
-            int polyVertIndex = fbxMesh->GetPolygonVertex(polyIndex, posInPoly);
-
-            // The UV index depends on the reference mode
-            uvIndex = useIndex ? uvs->GetIndexArray().GetAt(polyVertIndex) : polyVertIndex;
-        }
-        break;
-
-    case FbxGeometryElement::eByPolygonVertex:
-        if (meshVertexIndex < indexCount)
-        {
-            uvIndex = useIndex ? uvs->GetIndexArray().GetAt(meshVertexIndex) : meshVertexIndex;
-        }
-        break;
-
-    default:
-        // Only support eByPolygonVertex and eByControlPoint mappings
-        break;
-    }
-
-    vertex->hasTexCoord[uvSetIndex] = true;
-
-    // Store UV information in vertex
-    if (uvIndex != -1)
-    {
-        FbxVector2 uvValue = uvs->GetDirectArray().GetAt(uvIndex);
-        vertex->texCoord[uvSetIndex].x = (float)uvValue[0];
-        vertex->texCoord[uvSetIndex].y = (float)uvValue[1];
-    }
-}
-
-void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementNormalCount() > 0)
-    {
-        // Get only the first
-        FbxGeometryElementNormal* normal = fbxMesh->GetElementNormal(0);
-        FbxGeometryElement::EMappingMode mappingMode = normal->GetMappingMode();
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (normal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(controlPointIndex);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = normal->GetIndexArray().GetAt(controlPointIndex);
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(id);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (normal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(vertexIndex);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = normal->GetIndexArray().GetAt(vertexIndex);
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(id);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementTangentCount() > 0)
-    {
-        // Get only the first tangent
-        FbxGeometryElementTangent* tangent = fbxMesh->GetElementTangent(0);
-        FbxGeometryElement::EMappingMode mappingMode = tangent->GetMappingMode();
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (tangent->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(controlPointIndex);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = tangent->GetIndexArray().GetAt(controlPointIndex);
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(id);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (tangent->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(vertexIndex);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = tangent->GetIndexArray().GetAt(vertexIndex);
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(id);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementBinormalCount() > 0)
-    {
-        // Get only the first binormal.
-        FbxGeometryElementBinormal* binormal = fbxMesh->GetElementBinormal(0);
-        FbxGeometryElement::EMappingMode mappingMode = binormal->GetMappingMode();
-
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (binormal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(controlPointIndex);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = binormal->GetIndexArray().GetAt(controlPointIndex);
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(id);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (binormal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(vertexIndex);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = binormal->GetIndexArray().GetAt(vertexIndex);
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(id);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementVertexColorCount() > 0)
-    {
-        // Get only the first vertex color.
-        FbxGeometryElementVertexColor* vertexColor = fbxMesh->GetElementVertexColor(0);
-        FbxGeometryElement::EMappingMode mappingMode = vertexColor->GetMappingMode();
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (vertexColor->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(controlPointIndex);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = vertexColor->GetIndexArray().GetAt(controlPointIndex);
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(id);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (vertexColor->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(vertexIndex);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = vertexColor->GetIndexArray().GetAt(vertexIndex);
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(id);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex)
-{
-    size_t size = vertexWeights.size();
-
-    if (size >= 1)
-    {
-        vertex->hasWeights= true;
-        vertex->blendIndices.x = vertexWeights[0].x;
-        vertex->blendWeights.x = vertexWeights[0].y;
-    }
-    if (size >= 2)
-    {
-        vertex->blendIndices.y = vertexWeights[1].x;
-        vertex->blendWeights.y = vertexWeights[1].y;
-    }
-    if (size >= 3)
-    {
-        vertex->blendIndices.z = vertexWeights[2].x;
-        vertex->blendWeights.z = vertexWeights[2].y;
-    }
-    if (size >= 4)
-    {
-        vertex->blendIndices.w = vertexWeights[3].x;
-        vertex->blendWeights.w = vertexWeights[3].y;
-    }
-    //vertex->normalizeBlendWeight();
-}
-
-bool loadBlendWeights(FbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights)
-{
-    assert(fbxMesh);
-    const int vertexCount = fbxMesh->GetControlPointsCount();
-
-    FbxSkin* fbxSkin = NULL;
-    const int deformerCount = fbxMesh->GetDeformerCount();
-    for (int i = 0; i < deformerCount; ++i)
-    {
-        FbxDeformer* deformer = fbxMesh->GetDeformer(i);
-        if (deformer->GetDeformerType() == FbxDeformer::eSkin)
-        {
-            fbxSkin = static_cast<FbxSkin*>(deformer);
-            weights.resize(vertexCount);
-
-            const int clusterCount = fbxSkin->GetClusterCount();
-            for (int j = 0; j < clusterCount; ++j)
-            {
-                FbxCluster* cluster = fbxSkin->GetCluster(j);
-                assert(cluster);
-                const int vertexIndexCount = cluster->GetControlPointIndicesCount();
-                for (int k = 0; k < vertexIndexCount; ++k)
-                {
-                    int index = cluster->GetControlPointIndices()[k];
-                    if (index >= vertexCount)
-                    {
-                        continue;
-                    }
-
-                    double weight = cluster->GetControlPointWeights()[k];
-                    if (weight == 0.0)
-                    {
-                        continue;
-                    }
-                    weights[index].push_back(Vector2((float)j, (float)weight));
-                }
-            }
-            // Only the first skin deformer will be loaded.
-            // There probably won't be more than one.
-            break;
-        }
-    }
-    return fbxSkin != NULL;
-}
-
-void findMinMaxTime(FbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate)
-{
-    FbxTime start, stop;
-    FbxTimeSpan timeSpan;
-    animCurve->GetTimeInterval(timeSpan);
-    start = timeSpan.GetStart();
-    stop = timeSpan.GetStop();
-    *startTime = std::min(*startTime, (float)start.GetMilliSeconds());
-    *stopTime = std::max(*stopTime, (float)stop.GetMilliSeconds());
-    *frameRate = std::max(*frameRate, (float)stop.GetFrameRate(FbxTime::eDefaultMode));
-}
-
-void appendKeyFrame(FbxNode* fbxNode, AnimationChannel* channel, float time, const Vector3& scale, const Quaternion& rotation, const Vector3& translation)
-{
-    // Write key time
-    channel->getKeyTimes().push_back(time);
-
-    // Write key values
-    std::vector<float>& keyValues = channel->getKeyValues();
-    switch (channel->getTargetAttribute())
-    {
-        case Transform::ANIMATE_SCALE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_X:
-        {
-            keyValues.push_back(scale.x);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_Y:
-        {
-            keyValues.push_back(scale.y);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_Z:
-        {
-            keyValues.push_back(scale.z);
-        }
-        break;
-
-        case Transform::ANIMATE_ROTATE:
-        {
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE:
-        {
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE_X:
-        {
-            keyValues.push_back(translation.x);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE_Y:
-        {
-            keyValues.push_back(translation.y);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE_Z:
-        {
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_ROTATE_TRANSLATE:
-        {
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_TRANSLATE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_ROTATE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-        }
-        break;
-
-        default:
-        {
-            LOG(1, "Warning: Invalid animatoin target (%d) attribute for node: %s.\n", channel->getTargetAttribute(), fbxNode->GetName());
-        }
-        return;
-    }
-}
-
-void decompose(FbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation)
-{
-    FbxAMatrix fbxMatrix;
-    Matrix matrix;
-    FbxTime kTime;
-    kTime.SetMilliSeconds((FbxLongLong)time);
-    fbxMatrix = fbxNode->EvaluateLocalTransform(kTime);
-    copyMatrix(fbxMatrix, matrix);
-    matrix.decompose(scale, rotation, translation);
-}
-
-AnimationChannel* createAnimationChannel(FbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues)
-{
-    AnimationChannel* channel = new AnimationChannel();
-    channel->setTargetId(fbxNode->GetName());
-    channel->setKeyTimes(keyTimes);
-    channel->setKeyValues(keyValues);
-    channel->setInterpolation(AnimationChannel::LINEAR);
-    channel->setTargetAttribute(targetAttrib);
-    return channel;
-}
-
-void addScaleChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime)
-{
-    std::vector<float> keyTimes;
-    std::vector<float> keyValues;
-    Vector3 scale;
-    Quaternion rotation;
-    Vector3 translation;
-
-    decompose(fbxNode, startTime, &scale, &rotation, &translation);
-    keyTimes.push_back(startTime);
-    keyValues.push_back(scale.x);
-    keyValues.push_back(scale.y);
-    keyValues.push_back(scale.z);
-
-    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
-    keyTimes.push_back(stopTime);
-    keyValues.push_back(scale.x);
-    keyValues.push_back(scale.y);
-    keyValues.push_back(scale.z);
-
-    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_SCALE, keyTimes, keyValues);
-    animation->add(channel);
-}
-
-void addTranslateChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime)
-{
-    std::vector<float> keyTimes;
-    std::vector<float> keyValues;
-    Vector3 scale;
-    Quaternion rotation;
-    Vector3 translation;
-
-    decompose(fbxNode, startTime, &scale, &rotation, &translation);
-    keyTimes.push_back(startTime);
-    keyValues.push_back(translation.x);
-    keyValues.push_back(translation.y);
-    keyValues.push_back(translation.z);
-
-    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
-    keyTimes.push_back(stopTime);
-    keyValues.push_back(translation.x);
-    keyValues.push_back(translation.y);
-    keyValues.push_back(translation.z);
-
-    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_TRANSLATE, keyTimes, keyValues);
-    animation->add(channel);
-}
-
-void copyMatrix(const FbxMatrix& fbxMatrix, float* matrix)
-{
-    int i = 0;
-    for (int row = 0; row < 4; ++row)
-    {
-        for (int col = 0; col < 4; ++col)
-        {
-            matrix[i++] = (float)fbxMatrix.Get(row, col);
-        }
-    }
-}
-
-void copyMatrix(const FbxMatrix& fbxMatrix, Matrix& matrix)
-{
-    int i = 0;
-    for (int row = 0; row < 4; ++row)
-    {
-        for (int col = 0; col < 4; ++col)
-        {
-            matrix.m[i++] = (float)fbxMatrix.Get(row, col);
-        }
-    }
-}
-
-bool isGroupAnimationPossible(FbxScene* fbxScene)
-{
-    FbxNode* rootNode = fbxScene->GetRootNode();
-    if (rootNode)
-    {
-        if (isGroupAnimationPossible(rootNode))
-            return true;
-    }
-    return false;
-}
-
-bool isGroupAnimationPossible(FbxNode* fbxNode)
-{
-    if (fbxNode)
-    {
-        FbxMesh* fbxMesh = fbxNode->GetMesh();
-        if (isGroupAnimationPossible(fbxMesh))
-            return true;
-        const int childCount = fbxNode->GetChildCount();
-        for (int i = 0; i < childCount; ++i)
-        {
-            if (isGroupAnimationPossible(fbxNode->GetChild(i)))
-                return true;
-        }
-    }
-    return false;
-}
-
-bool isGroupAnimationPossible(FbxMesh* fbxMesh)
-{
-    if (fbxMesh)
-    {
-        const int deformerCount = fbxMesh->GetDeformerCount();
-        for (int i = 0; i < deformerCount; ++i)
-        {
-            FbxDeformer* deformer = fbxMesh->GetDeformer(i);
-            if (deformer->GetDeformerType() == FbxDeformer::eSkin)
-            {
-                FbxSkin* fbxSkin = static_cast<FbxSkin*>(deformer);
-                if (fbxSkin)
-                {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-#endif

+ 0 - 39
gameplay-encoder/src/Material.cpp

@@ -1,39 +0,0 @@
-#include "Base.h"
-#include "Material.h"
-
-namespace gameplay
-{
-
-Material::Material(void) :
-    _effect(NULL)
-{
-}
-
-Material::~Material(void)
-{
-}
-
-unsigned int Material::getTypeId(void) const
-{
-    return MATERIAL_ID;
-}
-const char* Material::getElementName(void) const
-{
-    return "Material";
-}
-
-void Material::writeBinary(FILE* file)
-{
-    Object::writeBinary(file);
-    //write(_parameters, file);
-    //write(_effect, file);
-}
-void Material::writeText(FILE* file)
-{
-    fprintElementStart(file);
-    //fprintfElement(file, "parameters", _parameters);
-    //fprintfElement(file, "effect", _effect);
-    fprintElementEnd(file);
-}
-
-}

+ 0 - 37
gameplay-encoder/src/Material.h

@@ -1,37 +0,0 @@
-#ifndef MATERIAL_H_
-#define MATERIAL_H_
-
-#include "Object.h"
-#include "MaterialParameter.h"
-#include "Effect.h"
-
-namespace gameplay
-{
-
-class Material : public Object
-{
-public:
-
-    /**
-     * Constructor.
-     */
-    Material(void);
-
-    /**
-     * Destructor.
-     */
-    virtual ~Material(void);
-
-    virtual unsigned int getTypeId(void) const;
-    virtual const char* getElementName(void) const;
-    virtual void writeBinary(FILE* file);
-    virtual void writeText(FILE* file);
-
-private:
-    std::list<MaterialParameter> _parameters;
-    Effect* _effect;
-};
-
-}
-
-#endif

+ 0 - 105
gameplay-encoder/src/Model.cpp

@@ -1,105 +0,0 @@
-#include "Base.h"
-#include "Model.h"
-
-namespace gameplay
-{
-
-Model::Model(void) :
-    _mesh(NULL),
-    _meshSkin(NULL)
-{
-}
-
-Model::~Model(void)
-{
-}
-
-unsigned int Model::getTypeId(void) const
-{
-    return MODEL_ID;
-}
-const char* Model::getElementName(void) const
-{
-    return "Model";
-}
-void Model::writeBinary(FILE* file)
-{
-    Object::writeBinary(file);
-
-    // xref:Mesh
-    if (_mesh != NULL)
-    {
-        _mesh->writeBinaryXref(file);
-    }
-    else
-    {
-        write((unsigned int)0, file);
-    }
-    // _meshSkin
-    // Write one unsigned char to indicate if this model has a skin
-    if (_meshSkin != NULL)
-    {
-        write((bool)true, file); // has a skin
-        _meshSkin->writeBinary(file);
-    }
-    else
-    {
-        write((bool)false, file); // doesn't have a skin
-    }
-    // materials[]
-    writeBinaryObjects(_materials, file);
-
-}
-
-void Model::writeText(FILE* file)
-{
-    // Compute mesh bounds before writing
-
-    fprintElementStart(file);
-    if (_mesh != NULL)
-    {
-        fprintfElement(file, "ref", _mesh->getId());
-    }
-    if (_meshSkin != NULL)
-    {
-        _meshSkin->writeText(file);
-    }
-    fprintElementEnd(file);
-}
-
-MeshSkin* Model::getSkin()
-{
-    return _meshSkin;
-}
-
-Mesh* Model::getMesh()
-{
-    return _mesh;
-}
-
-void Model::setMesh(Mesh* mesh)
-{
-    _mesh = mesh;
-
-    if (mesh)
-    {
-        mesh->model = this;
-    }
-
-    if (_mesh && _meshSkin)
-    {
-        _meshSkin->_mesh = _mesh;
-    }
-}
-
-void Model::setSkin(MeshSkin* skin)
-{
-    _meshSkin = skin;
-
-    if (_meshSkin)
-    {
-        _meshSkin->_mesh = _mesh;
-    }
-}
-
-}

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

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

BIN
gameplay-template/res/box.gpb


+ 2 - 2
gameplay.doxyfile

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

+ 38 - 38
gameplay.sln

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

+ 0 - 30
gameplay.workspace

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

+ 9 - 9
gameplay.xcworkspace/contents.xcworkspacedata

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

+ 0 - 248
gameplay/.cproject

@@ -124,134 +124,6 @@
 			</storageModule>
 			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
 		</cconfiguration>
-		<cconfiguration id="com.qnx.qcc.configuration.staticLib.profile.851611337">
-			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.qnx.qcc.configuration.staticLib.profile.851611337" moduleId="org.eclipse.cdt.core.settings" name="Device-Profile">
-				<externalSettings/>
-				<extensions>
-					<extension id="com.qnx.tools.ide.qde.core.QDEBynaryParser" point="org.eclipse.cdt.core.BinaryParser"/>
-					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
-				</extensions>
-			</storageModule>
-			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
-				<configuration artifactExtension="a" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.staticLib" buildProperties="org.eclipse.cdt.build.core.buildType=com.qnx.buildType.profile,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.staticLib" description="Build for Profiling" id="com.qnx.qcc.configuration.staticLib.profile.851611337" name="Device-Profile" parent="com.qnx.qcc.configuration.staticLib.profile">
-					<folderInfo id="com.qnx.qcc.configuration.staticLib.profile.851611337." name="/" resourcePath="">
-						<toolChain id="com.qnx.qcc.toolChain.staticLib.profile.1494216018" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
-							<option id="com.qnx.qcc.option.cpu.1727548796" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
-							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1288889025" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Profile}" id="cdt.managedbuild.target.gnu.builder.base.831558871" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
-							<tool id="com.qnx.qcc.tool.compiler.1281156842" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
-								<option id="com.qnx.qcc.option.compile.debug.626405189" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.profile2.1207899085" name="Build for Profiling (Function Instrumentation) (-finstrument-functions)" superClass="com.qnx.qcc.option.compiler.profile2" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.security.1649809766" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.defines.276653249" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
-									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.includePath.1503059677" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/lua/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.ccoptions.1956270067" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
-									<listOptionValue builtIn="false" value="-mfpu=neon"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.1366354884" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
-									<listOptionValue builtIn="false" value="-Wno-psabi"/>
-								</option>
-								<inputType id="com.qnx.qcc.inputType.compiler.81809638" superClass="com.qnx.qcc.inputType.compiler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.assembler.2145279747" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
-								<option id="com.qnx.qcc.option.assembler.debug.1503034293" name="Debug (-g)" superClass="com.qnx.qcc.option.assembler.debug" value="true" valueType="boolean"/>
-								<inputType id="com.qnx.qcc.inputType.assembler.1259311407" superClass="com.qnx.qcc.inputType.assembler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.linker.620187213" name="QCC Linker" superClass="com.qnx.qcc.tool.linker">
-								<option id="com.qnx.qcc.option.linker.debug.1398587920" name="Debug (-g)" superClass="com.qnx.qcc.option.linker.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.profile2.1507133006" name="Build for Profiling (Function Instrumentation) (-lprofiling)" superClass="com.qnx.qcc.option.linker.profile2" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.langcpp.67846367" name="C++ (-lang-c++)" superClass="com.qnx.qcc.option.linker.langcpp" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.security.427877434" name="Enhanced Security (-Wl,-z,relro -Wl,-z,now)" superClass="com.qnx.qcc.option.linker.security" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.libraryPaths.1941252718" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/lib"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/usr/lib"/>
-								</option>
-							</tool>
-							<tool id="com.qnx.qcc.tool.archiver.370183971" name="QCC Archiver" superClass="com.qnx.qcc.tool.archiver"/>
-						</toolChain>
-					</folderInfo>
-					<sourceEntries>
-						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
-					</sourceEntries>
-				</configuration>
-			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-		</cconfiguration>
-		<cconfiguration id="com.qnx.qcc.configuration.staticLib.coverage.304786667">
-			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.qnx.qcc.configuration.staticLib.coverage.304786667" moduleId="org.eclipse.cdt.core.settings" name="Device-Coverage">
-				<externalSettings/>
-				<extensions>
-					<extension id="com.qnx.tools.ide.qde.core.QDEBynaryParser" point="org.eclipse.cdt.core.BinaryParser"/>
-					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
-				</extensions>
-			</storageModule>
-			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
-				<configuration artifactExtension="a" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.staticLib" buildProperties="org.eclipse.cdt.build.core.buildType=com.qnx.buildType.coverage,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.staticLib" description="Build with Code Coverage" id="com.qnx.qcc.configuration.staticLib.coverage.304786667" name="Device-Coverage" parent="com.qnx.qcc.configuration.staticLib.coverage">
-					<folderInfo id="com.qnx.qcc.configuration.staticLib.coverage.304786667." name="/" resourcePath="">
-						<toolChain id="com.qnx.qcc.toolChain.staticLib.coverage.1939228131" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
-							<option id="com.qnx.qcc.option.cpu.832672244" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
-							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.987519072" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Coverage}" id="cdt.managedbuild.target.gnu.builder.base.121802503" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
-							<tool id="com.qnx.qcc.tool.compiler.306557636" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
-								<option id="com.qnx.qcc.option.compile.debug.168813234" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.coverage.1032644527" name="Build for Code Coverage (-Wc,-ftest-coverage -Wc,-fprofile-arcs)" superClass="com.qnx.qcc.option.compiler.coverage" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.security.1227516972" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.defines.374283024" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
-									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.includePath.1769677874" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/lua/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.ccoptions.47607907" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
-									<listOptionValue builtIn="false" value="-mfpu=neon"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.146547607" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
-									<listOptionValue builtIn="false" value="-Wno-psabi"/>
-								</option>
-								<inputType id="com.qnx.qcc.inputType.compiler.2007171407" superClass="com.qnx.qcc.inputType.compiler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.assembler.1537562121" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
-								<option id="com.qnx.qcc.option.assembler.debug.1135579455" name="Debug (-g)" superClass="com.qnx.qcc.option.assembler.debug" value="true" valueType="boolean"/>
-								<inputType id="com.qnx.qcc.inputType.assembler.1137191328" superClass="com.qnx.qcc.inputType.assembler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.linker.1976564730" name="QCC Linker" superClass="com.qnx.qcc.tool.linker">
-								<option id="com.qnx.qcc.option.linker.debug.483005272" name="Debug (-g)" superClass="com.qnx.qcc.option.linker.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.coverage.1325683096" name="Build for Code Coverage (-ftest-coverage -fprofile-arcs)" superClass="com.qnx.qcc.option.linker.coverage" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.langcpp.1336725462" name="C++ (-lang-c++)" superClass="com.qnx.qcc.option.linker.langcpp" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.security.261244208" name="Enhanced Security (-Wl,-z,relro -Wl,-z,now)" superClass="com.qnx.qcc.option.linker.security" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.libraryPaths.1333876349" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/lib"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/usr/lib"/>
-								</option>
-							</tool>
-							<tool id="com.qnx.qcc.tool.archiver.1121702347" name="QCC Archiver" superClass="com.qnx.qcc.tool.archiver"/>
-						</toolChain>
-					</folderInfo>
-					<sourceEntries>
-						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
-					</sourceEntries>
-				</configuration>
-			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-		</cconfiguration>
 		<cconfiguration id="com.qnx.qcc.configuration.staticLib.debug.559445444">
 			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.qnx.qcc.configuration.staticLib.debug.559445444" moduleId="org.eclipse.cdt.core.settings" name="Simulator">
 				<externalSettings/>
@@ -310,126 +182,6 @@
 			</storageModule>
 			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
 		</cconfiguration>
-		<cconfiguration id="com.qnx.qcc.configuration.staticLib.profile.191203500">
-			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.qnx.qcc.configuration.staticLib.profile.191203500" moduleId="org.eclipse.cdt.core.settings" name="Simulator-Profile">
-				<externalSettings/>
-				<extensions>
-					<extension id="com.qnx.tools.ide.qde.core.QDEBynaryParser" point="org.eclipse.cdt.core.BinaryParser"/>
-					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
-				</extensions>
-			</storageModule>
-			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
-				<configuration artifactExtension="a" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.staticLib" buildProperties="org.eclipse.cdt.build.core.buildType=com.qnx.buildType.profile,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.staticLib" description="Build for Profiling" id="com.qnx.qcc.configuration.staticLib.profile.191203500" name="Simulator-Profile" parent="com.qnx.qcc.configuration.staticLib.profile">
-					<folderInfo id="com.qnx.qcc.configuration.staticLib.profile.191203500." name="/" resourcePath="">
-						<toolChain id="com.qnx.qcc.toolChain.staticLib.profile.1691673400" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
-							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1730932164" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Simulator-Profile}" id="cdt.managedbuild.target.gnu.builder.base.854227640" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
-							<tool id="com.qnx.qcc.tool.compiler.417488704" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
-								<option id="com.qnx.qcc.option.compile.debug.1290366598" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.profile2.216911941" name="Build for Profiling (Function Instrumentation) (-finstrument-functions)" superClass="com.qnx.qcc.option.compiler.profile2" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.security.1329750381" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.defines.1679396285" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
-									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.includePath.513622172" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/lua/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.ccoptions.663337616" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions"/>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.288926109" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
-									<listOptionValue builtIn="false" value="-Wno-psabi"/>
-								</option>
-								<inputType id="com.qnx.qcc.inputType.compiler.1961855927" superClass="com.qnx.qcc.inputType.compiler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.assembler.1089440729" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
-								<option id="com.qnx.qcc.option.assembler.debug.1878429748" name="Debug (-g)" superClass="com.qnx.qcc.option.assembler.debug" value="true" valueType="boolean"/>
-								<inputType id="com.qnx.qcc.inputType.assembler.1343548216" superClass="com.qnx.qcc.inputType.assembler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.linker.198713701" name="QCC Linker" superClass="com.qnx.qcc.tool.linker">
-								<option id="com.qnx.qcc.option.linker.debug.1370984007" name="Debug (-g)" superClass="com.qnx.qcc.option.linker.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.profile2.745766388" name="Build for Profiling (Function Instrumentation) (-lprofiling)" superClass="com.qnx.qcc.option.linker.profile2" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.langcpp.888102264" name="C++ (-lang-c++)" superClass="com.qnx.qcc.option.linker.langcpp" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.security.2125784858" name="Enhanced Security (-Wl,-z,relro -Wl,-z,now)" superClass="com.qnx.qcc.option.linker.security" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.libraryPaths.1936736676" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/x86/lib"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/x86/usr/lib"/>
-								</option>
-							</tool>
-							<tool id="com.qnx.qcc.tool.archiver.621342655" name="QCC Archiver" superClass="com.qnx.qcc.tool.archiver"/>
-						</toolChain>
-					</folderInfo>
-					<sourceEntries>
-						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
-					</sourceEntries>
-				</configuration>
-			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-		</cconfiguration>
-		<cconfiguration id="com.qnx.qcc.configuration.staticLib.coverage.796584174">
-			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.qnx.qcc.configuration.staticLib.coverage.796584174" moduleId="org.eclipse.cdt.core.settings" name="Simulator-Coverage">
-				<externalSettings/>
-				<extensions>
-					<extension id="com.qnx.tools.ide.qde.core.QDEBynaryParser" point="org.eclipse.cdt.core.BinaryParser"/>
-					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
-				</extensions>
-			</storageModule>
-			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
-				<configuration artifactExtension="a" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.staticLib" buildProperties="org.eclipse.cdt.build.core.buildType=com.qnx.buildType.coverage,org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.staticLib" description="Build with Code Coverage" id="com.qnx.qcc.configuration.staticLib.coverage.796584174" name="Simulator-Coverage" parent="com.qnx.qcc.configuration.staticLib.coverage">
-					<folderInfo id="com.qnx.qcc.configuration.staticLib.coverage.796584174." name="/" resourcePath="">
-						<toolChain id="com.qnx.qcc.toolChain.staticLib.coverage.349788538" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
-							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1819308065" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Simulator-Coverage}" id="cdt.managedbuild.target.gnu.builder.base.66192685" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
-							<tool id="com.qnx.qcc.tool.compiler.563072865" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
-								<option id="com.qnx.qcc.option.compile.debug.1789973550" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.coverage.1289221781" name="Build for Code Coverage (-Wc,-ftest-coverage -Wc,-fprofile-arcs)" superClass="com.qnx.qcc.option.compiler.coverage" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.security.1296061040" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.compiler.defines.1925901823" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
-									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.includePath.1685994750" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/lua/include&quot;"/>
-									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
-								</option>
-								<option id="com.qnx.qcc.option.compiler.ccoptions.346770186" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions"/>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.1085566269" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList">
-									<listOptionValue builtIn="false" value="-Wno-psabi"/>
-								</option>
-								<inputType id="com.qnx.qcc.inputType.compiler.1658185881" superClass="com.qnx.qcc.inputType.compiler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.assembler.746786008" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
-								<option id="com.qnx.qcc.option.assembler.debug.801192729" name="Debug (-g)" superClass="com.qnx.qcc.option.assembler.debug" value="true" valueType="boolean"/>
-								<inputType id="com.qnx.qcc.inputType.assembler.1060784613" superClass="com.qnx.qcc.inputType.assembler"/>
-							</tool>
-							<tool id="com.qnx.qcc.tool.linker.499344619" name="QCC Linker" superClass="com.qnx.qcc.tool.linker">
-								<option id="com.qnx.qcc.option.linker.debug.1036858603" name="Debug (-g)" superClass="com.qnx.qcc.option.linker.debug" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.coverage.120064975" name="Build for Code Coverage (-ftest-coverage -fprofile-arcs)" superClass="com.qnx.qcc.option.linker.coverage" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.langcpp.732448976" name="C++ (-lang-c++)" superClass="com.qnx.qcc.option.linker.langcpp" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.security.2060919956" name="Enhanced Security (-Wl,-z,relro -Wl,-z,now)" superClass="com.qnx.qcc.option.linker.security" value="true" valueType="boolean"/>
-								<option id="com.qnx.qcc.option.linker.libraryPaths.2023922042" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/x86/lib"/>
-									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/x86/usr/lib"/>
-								</option>
-							</tool>
-							<tool id="com.qnx.qcc.tool.archiver.1860081202" name="QCC Archiver" superClass="com.qnx.qcc.tool.archiver"/>
-						</toolChain>
-					</folderInfo>
-					<sourceEntries>
-						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
-					</sourceEntries>
-				</configuration>
-			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-		</cconfiguration>
 	</storageModule>
 	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
 		<project id="gameplay.null.436970955" name="gameplay"/>

+ 8 - 0
gameplay/CMakeLists.txt

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

+ 2 - 5
gameplay/android/build.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project name="gameplay-android" default="help">
+<project name="gameplay" default="help">
 
     <!-- The local.properties file is created and updated by the 'android' tool.
          It contains the path to the SDK. It should *NOT* be checked into
@@ -40,10 +40,7 @@
     <loadproperties srcFile="project.properties" />
 
     <!-- quick check on sdk.dir -->
-    <fail
-            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
-            unless="sdk.dir"
-    />
+    <fail message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var" unless="sdk.dir" />
 
 
 <!-- extension targets. Uncomment the ones where you want to do custom work

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

@@ -1,17 +1,4 @@
-# Copyright (C) 2009 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
+
 LOCAL_PATH := $(call my-dir)/../../src
 
 include $(CLEAR_VARS)
@@ -54,6 +41,7 @@ LOCAL_SRC_FILES := \
     Gamepad.cpp \
     HeightField.cpp \
     Image.cpp \
+	ImageControl.cpp \
     Joint.cpp \
     Joystick.cpp \
     Label.cpp \
@@ -87,6 +75,7 @@ LOCAL_SRC_FILES := \
     PhysicsVehicle.cpp \
     PhysicsVehicleWheel.cpp \
     Plane.cpp \
+    Platform.cpp \
     PlatformAndroid.cpp \
     Properties.cpp \
     Quaternion.cpp \
@@ -178,6 +167,7 @@ LOCAL_SRC_FILES := \
     lua/lua_Global.cpp \
     lua/lua_HeightField.cpp \
     lua/lua_Image.cpp \
+    lua/lua_ImageControl.cpp \
     lua/lua_ImageFormat.cpp \
     lua/lua_Joint.cpp \
     lua/lua_Joystick.cpp \
@@ -247,6 +237,7 @@ LOCAL_SRC_FILES := \
     lua/lua_RenderState.cpp \
     lua/lua_RenderStateAutoBinding.cpp \
     lua/lua_RenderStateBlend.cpp \
+    lua/lua_RenderStateCullFaceSide.cpp \
     lua/lua_RenderStateDepthFunction.cpp \
     lua/lua_RenderStateStateBlock.cpp \
     lua/lua_RenderTarget.cpp \
@@ -260,6 +251,7 @@ LOCAL_SRC_FILES := \
     lua/lua_Technique.cpp \
     lua/lua_Terrain.cpp \
     lua/lua_TerrainFlags.cpp \
+    lua/lua_TerrainListener.cpp \
     lua/lua_TextBox.cpp \
     lua/lua_Texture.cpp \
     lua/lua_TextureFilter.cpp \

+ 26 - 11
gameplay/gameplay.vcxproj

@@ -91,6 +91,7 @@
     <ClCompile Include="src\gameplay-main-windows.cpp" />
     <ClCompile Include="src\HeightField.cpp" />
     <ClCompile Include="src\Image.cpp" />
+    <ClCompile Include="src\ImageControl.cpp" />
     <ClCompile Include="src\Joint.cpp" />
     <ClCompile Include="src\Joystick.cpp" />
     <ClCompile Include="src\Label.cpp" />
@@ -158,6 +159,7 @@
     <ClCompile Include="src\lua\lua_Global.cpp" />
     <ClCompile Include="src\lua\lua_HeightField.cpp" />
     <ClCompile Include="src\lua\lua_Image.cpp" />
+    <ClCompile Include="src\lua\lua_ImageControl.cpp" />
     <ClCompile Include="src\lua\lua_ImageFormat.cpp" />
     <ClCompile Include="src\lua\lua_Joint.cpp" />
     <ClCompile Include="src\lua\lua_Joystick.cpp" />
@@ -227,6 +229,7 @@
     <ClCompile Include="src\lua\lua_RenderState.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateAutoBinding.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateBlend.cpp" />
+    <ClCompile Include="src\lua\lua_RenderStateCullFaceSide.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateDepthFunction.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateStateBlock.cpp" />
     <ClCompile Include="src\lua\lua_RenderTarget.cpp" />
@@ -240,6 +243,7 @@
     <ClCompile Include="src\lua\lua_Technique.cpp" />
     <ClCompile Include="src\lua\lua_Terrain.cpp" />
     <ClCompile Include="src\lua\lua_TerrainFlags.cpp" />
+    <ClCompile Include="src\lua\lua_TerrainListener.cpp" />
     <ClCompile Include="src\lua\lua_TextBox.cpp" />
     <ClCompile Include="src\lua\lua_Texture.cpp" />
     <ClCompile Include="src\lua\lua_TextureFilter.cpp" />
@@ -292,6 +296,7 @@
     <ClCompile Include="src\PhysicsVehicle.cpp" />
     <ClCompile Include="src\PhysicsVehicleWheel.cpp" />
     <ClCompile Include="src\Plane.cpp" />
+    <ClCompile Include="src\Platform.cpp" />
     <ClCompile Include="src\PlatformAndroid.cpp" />
     <ClCompile Include="src\PlatformBlackBerry.cpp" />
     <ClCompile Include="src\PlatformLinux.cpp" />
@@ -366,6 +371,7 @@
     <ClInclude Include="src\Gesture.h" />
     <ClInclude Include="src\HeightField.h" />
     <ClInclude Include="src\Image.h" />
+    <ClInclude Include="src\ImageControl.h" />
     <ClInclude Include="src\Joint.h" />
     <ClInclude Include="src\Joystick.h" />
     <ClInclude Include="src\Keyboard.h" />
@@ -434,6 +440,7 @@
     <ClInclude Include="src\lua\lua_Global.h" />
     <ClInclude Include="src\lua\lua_HeightField.h" />
     <ClInclude Include="src\lua\lua_Image.h" />
+    <ClInclude Include="src\lua\lua_ImageControl.h" />
     <ClInclude Include="src\lua\lua_ImageFormat.h" />
     <ClInclude Include="src\lua\lua_Joint.h" />
     <ClInclude Include="src\lua\lua_Joystick.h" />
@@ -503,6 +510,7 @@
     <ClInclude Include="src\lua\lua_RenderState.h" />
     <ClInclude Include="src\lua\lua_RenderStateAutoBinding.h" />
     <ClInclude Include="src\lua\lua_RenderStateBlend.h" />
+    <ClInclude Include="src\lua\lua_RenderStateCullFaceSide.h" />
     <ClInclude Include="src\lua\lua_RenderStateDepthFunction.h" />
     <ClInclude Include="src\lua\lua_RenderStateStateBlock.h" />
     <ClInclude Include="src\lua\lua_RenderTarget.h" />
@@ -516,6 +524,7 @@
     <ClInclude Include="src\lua\lua_Technique.h" />
     <ClInclude Include="src\lua\lua_Terrain.h" />
     <ClInclude Include="src\lua\lua_TerrainFlags.h" />
+    <ClInclude Include="src\lua\lua_TerrainListener.h" />
     <ClInclude Include="src\lua\lua_TextBox.h" />
     <ClInclude Include="src\lua\lua_Texture.h" />
     <ClInclude Include="src\lua\lua_TextureFilter.h" />
@@ -612,15 +621,21 @@
     <None Include="res\shaders\colored-unlit.vert" />
     <None Include="res\shaders\colored.frag" />
     <None Include="res\shaders\colored.vert" />
-    <None Include="res\shaders\lib\attributes-skinning.vert" />
-    <None Include="res\shaders\lib\attributes.vert" />
-    <None Include="res\shaders\lib\lighting-directional.frag" />
-    <None Include="res\shaders\lib\lighting-directional.vert" />
-    <None Include="res\shaders\lib\lighting-point.frag" />
-    <None Include="res\shaders\lib\lighting-point.vert" />
-    <None Include="res\shaders\lib\lighting-spot.frag" />
-    <None Include="res\shaders\lib\lighting-spot.vert" />
-    <None Include="res\shaders\lib\lighting.frag" />
+    <None Include="res\shaders\font.frag" />
+    <None Include="res\shaders\font.vert" />
+    <None Include="res\shaders\form.frag" />
+    <None Include="res\shaders\form.vert" />
+    <None Include="res\shaders\lighting-directional.frag" />
+    <None Include="res\shaders\lighting-directional.vert" />
+    <None Include="res\shaders\lighting-point.frag" />
+    <None Include="res\shaders\lighting-point.vert" />
+    <None Include="res\shaders\lighting-spot.frag" />
+    <None Include="res\shaders\lighting-spot.vert" />
+    <None Include="res\shaders\lighting.frag" />
+    <None Include="res\shaders\skinning-none.vert" />
+    <None Include="res\shaders\skinning.vert" />
+    <None Include="res\shaders\sprite.frag" />
+    <None Include="res\shaders\sprite.vert" />
     <None Include="res\shaders\terrain.frag" />
     <None Include="res\shaders\terrain.vert" />
     <None Include="res\shaders\textured-bumped.frag" />
@@ -887,7 +902,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>__BB10__;_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>USE_BLACKBERRY_GAMEPAD;_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)src;..\external-deps\lua\include;..\external-deps\bullet\include;..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\oggvorbis\include;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>
       </RuntimeTypeInfo>
@@ -1081,4 +1096,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

+ 70 - 28
gameplay/gameplay.vcxproj.filters

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 431 - 426
gameplay/gameplay.xcodeproj/project.pbxproj


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

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

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


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

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

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

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

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

@@ -66,7 +66,7 @@ void main()
     gl_FragColor.rgb = getLitPixel();
     
 	#if defined(MODULATE_COLOR)
-    gl_FragColor.a *= u_modulateColor;
+    gl_FragColor *= u_modulateColor;
     #endif
 	#if defined(MODULATE_ALPHA)
     gl_FragColor.a *= u_modulateAlpha;

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

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

+ 12 - 1
gameplay/src/AIStateMachine.cpp

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

+ 1 - 1
gameplay/src/AbsoluteLayout.cpp

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

+ 3 - 1
gameplay/src/Animation.cpp

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

+ 70 - 37
gameplay/src/AnimationClip.cpp

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

+ 18 - 1
gameplay/src/AnimationClip.h

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

+ 3 - 1
gameplay/src/AnimationController.cpp

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

+ 1 - 0
gameplay/src/AudioBuffer.cpp

@@ -327,6 +327,7 @@ bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
             }
         }
     }
+    return false;
 }
 
 bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)

+ 4 - 4
gameplay/src/AudioSource.cpp

@@ -77,18 +77,18 @@ AudioSource* AudioSource::create(Properties* properties)
         return NULL;
     }
 
-    const char* path = properties->getString("path");
-    if (path == NULL)
+    std::string path;
+    if (!properties->getPath("path", &path))
     {
         GP_ERROR("Audio file failed to load; the file path was not specified.");
         return NULL;
     }
 
     // Create the audio source.
-    AudioSource* audio = AudioSource::create(path);
+    AudioSource* audio = AudioSource::create(path.c_str());
     if (audio == NULL)
     {
-        GP_ERROR("Audio file '%s' failed to load properly.", path);
+        GP_ERROR("Audio file '%s' failed to load properly.", path.c_str());
         return NULL;
     }
 

+ 32 - 2
gameplay/src/Bundle.cpp

@@ -286,6 +286,24 @@ const char* Bundle::getIdFromOffset(unsigned int offset) const
     return NULL;
 }
 
+const std::string& Bundle::getMaterialPath()
+{
+    if (_materialPath.empty())
+    {
+        int pos = _path.find_last_of('.');
+        if (pos > 2)
+        {
+            _materialPath = _path.substr(0, pos);
+            _materialPath.append(".material");
+            if (!FileSystem::fileExists(_materialPath.c_str()))
+            {
+                _materialPath.clear();
+            }
+        }
+    }
+    return _materialPath;
+}
+
 Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
 {
     Reference* ref = find(id);
@@ -968,8 +986,20 @@ Model* Bundle::readModel(const char* nodeId)
             }
             if (materialCount > 0)
             {
-                // TODO: Material loading not supported yet.
-                GP_WARN("Material loading is not yet supported.");
+                for (unsigned int i = 0; i < materialCount; ++i)
+                {
+                    std::string materialName = readString(_stream);
+                    std::string materialPath = getMaterialPath();
+                    materialPath.append("#");
+                    materialPath.append(materialName);
+                    Material* material = Material::create(materialPath.c_str());
+                    if (material)
+                    {
+                        int partIndex = model->getMesh()->getPartCount() > 0 ? i : -1;
+                        model->setMaterial(material, partIndex);
+                        SAFE_RELEASE(material);
+                    }
+                }
             }
             return model;
         }

+ 8 - 0
gameplay/src/Bundle.h

@@ -189,6 +189,13 @@ private:
      */
     const char* getIdFromOffset(unsigned int offset) const;
 
+    /**
+     * Gets the path to the bundle's default material file, if it exists.
+     * 
+     * @return The bundle's default material path. Returns an empty string if the default material does not exist.
+     */
+    const std::string& getMaterialPath();
+
     /**
      * Seeks the file pointer to the object with the given ID and type
      * and returns the relevant Reference.
@@ -427,6 +434,7 @@ private:
     bool skipNode();
 
     std::string _path;
+    std::string _materialPath;
     unsigned int _referenceCount;
     Reference* _references;
     Stream* _stream;

+ 56 - 9
gameplay/src/Button.cpp

@@ -50,11 +50,8 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
                 _contactIndex = (int) contactIndex;
-
+                notifyListeners(Control::Listener::PRESS);
                 setState(Control::ACTIVE);
-
-                notifyListeners(Listener::PRESS);
-
                 return _consumeInputEvents;
             }
             else
@@ -68,14 +65,13 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         if (_contactIndex == (int) contactIndex)
         {
             _contactIndex = INVALID_CONTACT_INDEX;
-            notifyListeners(Listener::RELEASE);
+            notifyListeners(Control::Listener::RELEASE);
             if (!_parent->isScrolling() &&
                 x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
+                notifyListeners(Control::Listener::CLICK);
                 setState(Control::FOCUS);
-
-                notifyListeners(Listener::CLICK);
             }
             else
             {
@@ -85,14 +81,65 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         }
         break;
     case Touch::TOUCH_MOVE:
-        if (_contactIndex == (int) contactIndex)
-            return _consumeInputEvents;
+        return Control::touchEvent(evt, x, y, contactIndex);
+    }
+
+    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;
 }
 
+bool Button::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+    if (evt == Keyboard::KEY_PRESS && key == Keyboard::KEY_RETURN)
+    {
+        notifyListeners(Control::Listener::PRESS);
+        setState(Control::ACTIVE);
+        return _consumeInputEvents;
+    }
+    else if (_state == ACTIVE && evt == Keyboard::KEY_RELEASE && key == Keyboard::KEY_RETURN)
+    {
+        notifyListeners(Control::Listener::RELEASE);
+        notifyListeners(Control::Listener::CLICK);
+        setState(Control::FOCUS);
+        return _consumeInputEvents;
+    }
+
+    return false;
+}
+
 const char* Button::getType() const
 {
     return "button";

+ 15 - 0
gameplay/src/Button.h

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

+ 57 - 22
gameplay/src/Camera.cpp

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

+ 41 - 1
gameplay/src/Camera.h

@@ -191,6 +191,26 @@ public:
      */
     const Matrix& getProjectionMatrix() const;
 
+    /**
+     * Sets a custom projection matrix to be used by the camera.
+     *
+     * Setting a custom projection matrix results in the internally 
+     * computed projection matrix being completely overriden until
+     * the resetProjectionMatrix method is called. A custom projection
+     * matrix is normally not neccessary, but can be used for special
+     * projection effects, such as setting an oblique view frustum
+     * for near plane clipping.
+     *
+     * @param matrix Custom projection matrix.
+     */
+    void setProjectionMatrix(const Matrix& matrix);
+
+    /**
+     * Resets the camera to use the internally computed projection matrix
+     * instead of any previously specified user-defined matrix.
+     */
+    void resetProjectionMatrix();
+
     /**
      * Gets the camera's view * projection matrix.
      *
@@ -220,9 +240,29 @@ public:
      * @param x The returned viewport x coordinate.
      * @param y The returned viewport y coordinate.
      * @param depth The returned pixel depth (can be NULL).
+     *
+     * @script{ignore}
      */
     void project(const Rectangle& viewport, const Vector3& position, float* x, float* y, float* depth = NULL) const;
 
+    /**
+     * Projects the specified world position into the viewport coordinates.
+     *
+     * @param viewport The viewport rectangle to use.
+     * @param position The world space position.
+     * @param out Populated with the resulting screen-space position.
+     */
+    void project(const Rectangle& viewport, const Vector3& position, Vector2* out) const;
+
+    /**
+     * Projects the specified world position into the viewport coordinates.
+     *
+     * @param viewport The viewport rectangle to use.
+     * @param position The world space position.
+     * @param out Populated with the resulting screen-space position, with the pixel depth in the Z coordinate.
+     */
+    void project(const Rectangle& viewport, const Vector3& position, Vector3* out) const;
+
     /**
      * Converts a viewport-space coordinate to a world-space position for the given depth value.
      *
@@ -299,7 +339,7 @@ private:
     mutable Matrix _inverseView;
     mutable Matrix _inverseViewProjection;
     mutable Frustum _bounds;
-    mutable int _dirtyBits;
+    mutable int _bits;
     Node* _node;
 };
 

+ 31 - 0
gameplay/src/CheckBox.cpp

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

+ 15 - 0
gameplay/src/CheckBox.h

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

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 728 - 73
gameplay/src/Container.cpp


+ 111 - 19
gameplay/src/Container.h

@@ -3,6 +3,7 @@
 
 #include "Control.h"
 #include "Layout.h"
+#include "TimeListener.h"
 
 namespace gameplay
 {
@@ -26,7 +27,10 @@ namespace gameplay
          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'
          scroll      = <Container::Scroll constant> // Whether scrolling is allowed and in which directions.
-         scrollBarsAutoHide = <bool>    // Whether scrollbars fade out when not in use.
+         scrollBarsAutoHide = <bool>        // Whether scrollbars fade out when not in use.
+         scrollingFriction = <float>        // Friction applied to inertial scrolling.
+         scrollWheelRequiresFocus = <bool>  // Whether focus or hover state handles scroll-wheel events.
+         scrollWheelSpeed = <float>         // Speed to scroll at on a scroll-wheel event.
          consumeEvents = <bool>             // Whether the container propagates input events to the Game's input event handler. Default is true.
 
          // All the nested controls within this container.
@@ -44,7 +48,7 @@ namespace gameplay
     }
  @endverbatim
  */
-class Container : public Control
+class Container : public Control, TimeListener
 {
 
 public:
@@ -182,6 +186,27 @@ public:
      */
     bool isScrolling() const;
 
+    /**
+     * Get the friction applied to scrolling velocity for this container.
+     */
+    float getScrollingFriction() const;
+
+    /**
+     * Get the friction applied to scrolling velocity for this container.
+     * A higher value will bring the viewport to a stop sooner.
+     */
+    void setScrollingFriction(float friction);
+
+    /**
+     * Get the speed added to scrolling velocity on a scroll-wheel event.
+     */
+    float getScrollWheelSpeed() const;
+
+    /**
+     * Set the speed added to scrolling velocity on a scroll-wheel event.
+     */
+    void setScrollWheelSpeed(float speed);
+
     /**
      * @see AnimationTarget::getAnimation
      */
@@ -197,6 +222,21 @@ public:
      */
     const char* getType() const;
 
+    /**
+     * Get whether this container requires focus in order to handle scroll-wheel events.
+     */
+    bool getScrollWheelRequiresFocus() const;
+
+    /**
+     * Set whether this container requires focus in order to handle scroll-wheel events.
+     * If this property is set to true, scroll-wheel events will only be handled when the container has focus.
+     * If this property is set tofalse, scroll-wheel events will only be handled
+     * when the container is in the HOVER state.
+     *
+     * @param required Whether focus is required in order to handle scroll-wheel events.
+     */
+    void setScrollWheelRequiresFocus(bool required);
+
     /**
      * @see AnimationTarget::getAnimationPropertyComponentCount
      */
@@ -212,6 +252,13 @@ public:
      */
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
+    /**
+     * @see TimeListener::timeEvent
+     *
+     * @script{ignore}
+     */
+    void timeEvent(long timeDiff, void* cookie);
+
 protected:
 
     /**
@@ -291,10 +338,17 @@ protected:
      *
      * @return True if the mouse event is consumed or false if it is not consumed.
      *
-     * @see Mouse::MouseEvent
+     * @see Mouse::mouseEvent
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Gets a Layout::Type enum from a matching string.
      *
@@ -334,6 +388,13 @@ protected:
      */
     void updateScroll();
 
+    /**
+     * Sorts controls by Z-Order (for absolute layouts only).
+     * This method is used by controls to notify their parent container when
+     * their Z-Index changes.
+     */
+    void sortControls();
+
     /**
      * Applies touch events to scroll state.
      *
@@ -400,7 +461,7 @@ protected:
      */
     Theme::ThemeImage* _scrollBarTopCap;
     /**
-     * Scrollbar vertical image.
+     * Scrollbar vertical track image.
      */
     Theme::ThemeImage* _scrollBarVertical;
     /**
@@ -412,7 +473,7 @@ protected:
      */
     Theme::ThemeImage* _scrollBarLeftCap;
     /**
-     * Scrollbar horizontal image.
+     * Scrollbar horizontal track image.
      */
     Theme::ThemeImage* _scrollBarHorizontal;
     /**
@@ -424,7 +485,7 @@ protected:
      */
     Scroll _scroll;
     /** 
-     * Scroll bar bounds
+     * Scroll bar bounds.
      */
     Rectangle _scrollBarBounds;
     /** 
@@ -432,7 +493,7 @@ protected:
      */
     Vector2 _scrollPosition;
     /** 
-     * Should the scrollbars auto hide. Default is false.
+     * Whether the scrollbars should auto-hide. Default is false.
      */
     bool _scrollBarsAutoHide;
     /** 
@@ -444,11 +505,11 @@ protected:
      */
     bool _scrolling;
     /** 
-     * First scrolling touch x position
+     * First scrolling touch x position.
      */
     int _scrollingVeryFirstX;
     /**
-     * First scrolling touch y position
+     * First scrolling touch y position.
      */
     int _scrollingVeryFirstY;
     /**
@@ -460,33 +521,37 @@ protected:
      */ 
     int _scrollingFirstY;
     /** 
-     * The last y position when scrolling
+     * The last y position when scrolling.
      */ 
     int _scrollingLastX;
     /** 
-     * The last x position when scrolling
+     * The last x position when scrolling.
      */ 
     int _scrollingLastY;
     /** 
-     * Time we started scrolling in the x
+     * Time we started scrolling horizontally.
      */ 
     double _scrollingStartTimeX;
     /** 
-     * Time we started scrolling in the y
+     * Time we started scrolling vertically.
      */ 
     double _scrollingStartTimeY;
     /** 
-     * The last time we were scrolling
+     * The last time we were scrolling.
      */
     double _scrollingLastTime;
     /** 
-     * Speed to continue scrolling at after touch release.
+     * Speed to continue scrolling at after touch release or a scroll-wheel event.
      */ 
     Vector2 _scrollingVelocity;
     /** 
      * Friction dampens velocity.
      */ 
     float _scrollingFriction;
+    /**
+     * Amount to add to scrolling velocity on a scroll-wheel event;
+     */
+    float _scrollWheelSpeed;
     /** 
      * Are we scrolling to the right?
      */ 
@@ -495,12 +560,10 @@ protected:
      * Are we scrolling down?
      */ 
     bool _scrollingDown;
-
     /**
      * Locked to scrolling vertically by grabbing the scrollbar with the mouse.
      */
     bool _scrollingMouseVertically;
-
     /**
      * Locked to scrolling horizontally by grabbing the scrollbar with the mouse.
      */
@@ -513,17 +576,46 @@ private:
      */
     Container(const Container& copy);
 
+    enum Direction
+    {
+        UP = 0x01,
+        DOWN = 0x02,
+        LEFT = 0x04,
+        RIGHT = 0x08,
+        NEXT = 0x10
+    };
+
+    // Returns true on success; false if there are no controls to focus on,
+    // in which case scrolling can be initiated.
+    bool moveFocus(Direction direction, Control* outsideControl = NULL);
+
+    void guaranteeFocus(Control* inFocus);
+
+    // Starts scrolling at the given horizontal and vertical speeds.
+    void startScrolling(float x, float y, bool resetTime = true);
+
+    void stopScrolling();
+
     AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
     int _focusIndexDefault;
     int _focusIndexMax;
+    unsigned int _focusPressed;
+    bool _selectButtonDown;
+    double _lastFrameTime;
+
+    // Timing information for repeating focus changes.
+    bool _focusChangeRepeat;
+    double _focusChangeStartTime;
+    double _focusChangeRepeatDelay;
+    unsigned int _focusChangeCount;
+    Direction _direction;
 
     float _totalWidth;
     float _totalHeight;
-
     int _contactIndices;
-
     bool _initializedWithScroll;
+    bool _scrollWheelRequiresFocus;
 };
 
 }

+ 136 - 38
gameplay/src/Control.cpp

@@ -7,8 +7,8 @@ namespace gameplay
 
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
-    _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(0), _parent(NULL), _styleOverridden(false), _skin(NULL)
+    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(false), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
+    _zIndex(-1), _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _parent(NULL), _styleOverridden(false), _skin(NULL), _previousState(NORMAL)
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
 }
@@ -17,9 +17,9 @@ Control::~Control()
 {
     if (_listeners)
     {
-        for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
+        for (std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
         {
-            std::list<Listener*>* list = itr->second;
+            std::list<Control::Listener*>* list = itr->second;
             SAFE_DELETE(list);
         }
         SAFE_DELETE(_listeners);
@@ -41,7 +41,7 @@ void Control::initialize(Theme::Style* style, Properties* properties)
     _autoWidth = properties->getBool("autoWidth");
     _autoHeight = properties->getBool("autoHeight");
 
-    _consumeInputEvents = properties->getBool("consumeInputEvents", true);
+    _consumeInputEvents = properties->getBool("consumeInputEvents", false);
 
     _visible = properties->getBool("visible", true);
 
@@ -115,6 +115,10 @@ void Control::initialize(Theme::Style* style, Properties* properties)
         {
             overrideThemedProperties(innerSpace, DISABLED);
         }
+        else if (spaceName == "STATEHOVER")
+        {
+            overrideThemedProperties(innerSpace, HOVER);
+        }
         else if (spaceName == "MARGIN")
         {
             setMargin(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
@@ -187,6 +191,11 @@ const Rectangle& Control::getBounds() const
     return _bounds;
 }
 
+const Rectangle& Control::getAbsoluteBounds() const
+{
+    return _absoluteBounds;
+}
+
 float Control::getX() const
 {
     return _bounds.x;
@@ -266,6 +275,11 @@ bool Control::isVisible() const
     return _visible;
 }
 
+bool Control::hasFocus() const
+{
+    return (_state == FOCUS || (_state == HOVER && _previousState == FOCUS));
+}
+
 void Control::setOpacity(float opacity, unsigned char states)
 {
     overrideStyle();
@@ -648,6 +662,8 @@ Theme::Style::OverlayType Control::getOverlayType() const
         return Theme::Style::OVERLAY_ACTIVE;
     case Control::DISABLED:
         return Theme::Style::OVERLAY_DISABLED;
+    case Control::HOVER:
+        return Theme::Style::OVERLAY_HOVER;
     default:
         return Theme::Style::OVERLAY_NORMAL;
     }
@@ -691,29 +707,29 @@ void Control::addListener(Control::Listener* listener, int eventFlags)
 {
     GP_ASSERT(listener);
 
-    if ((eventFlags & Listener::PRESS) == Listener::PRESS)
+    if ((eventFlags & Control::Listener::PRESS) == Control::Listener::PRESS)
     {
-        addSpecificListener(listener, Listener::PRESS);
+        addSpecificListener(listener, Control::Listener::PRESS);
     }
 
-    if ((eventFlags & Listener::RELEASE) == Listener::RELEASE)
+    if ((eventFlags & Control::Listener::RELEASE) == Control::Listener::RELEASE)
     {
-        addSpecificListener(listener, Listener::RELEASE);
+        addSpecificListener(listener, Control::Listener::RELEASE);
     }
 
-    if ((eventFlags & Listener::CLICK) == Listener::CLICK)
+    if ((eventFlags & Control::Listener::CLICK) == Control::Listener::CLICK)
     {
-        addSpecificListener(listener, Listener::CLICK);
+        addSpecificListener(listener, Control::Listener::CLICK);
     }
 
-    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
+    if ((eventFlags & Control::Listener::VALUE_CHANGED) == Control::Listener::VALUE_CHANGED)
     {
-        addSpecificListener(listener, Listener::VALUE_CHANGED);
+        addSpecificListener(listener, Control::Listener::VALUE_CHANGED);
     }
 
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
-        addSpecificListener(listener, Listener::TEXT_CHANGED);
+        addSpecificListener(listener, Control::Listener::TEXT_CHANGED);
     }
 }
 
@@ -722,13 +738,13 @@ void Control::removeListener(Control::Listener* listener)
     if (_listeners == NULL || listener == NULL)
         return;
 
-    for (std::map<Listener::EventType, std::list<Listener*>*>::iterator itr = _listeners->begin(); itr != _listeners->end();)
+    for (std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::iterator itr = _listeners->begin(); itr != _listeners->end();)
     {
         itr->second->remove(listener);
 
         if(itr->second->empty())
         {
-            std::list<Listener*>* list = itr->second;
+            std::list<Control::Listener*>* list = itr->second;
             _listeners->erase(itr++);
             SAFE_DELETE(list);
         }
@@ -740,23 +756,23 @@ void Control::removeListener(Control::Listener* listener)
         SAFE_DELETE(_listeners);
 }
 
-void Control::addSpecificListener(Control::Listener* listener, Listener::EventType eventType)
+void Control::addSpecificListener(Control::Listener* listener, Control::Listener::EventType eventType)
 {
     GP_ASSERT(listener);
 
     if (!_listeners)
     {
-        _listeners = new std::map<Listener::EventType, std::list<Listener*>*>();
+        _listeners = new std::map<Control::Listener::EventType, std::list<Control::Listener*>*>();
     }
 
-    std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
+    std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->find(eventType);
     if (itr == _listeners->end())
     {
-        _listeners->insert(std::make_pair(eventType, new std::list<Listener*>()));
+        _listeners->insert(std::make_pair(eventType, new std::list<Control::Listener*>()));
         itr = _listeners->find(eventType);
     }
 
-    std::list<Listener*>* listenerList = itr->second;
+    std::list<Control::Listener*>* listenerList = itr->second;
     listenerList->push_back(listener);
 }
 
@@ -773,7 +789,7 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
             _contactIndex = (int) contactIndex;
 
-            notifyListeners(Listener::PRESS);
+            notifyListeners(Control::Listener::PRESS);
 
             return _consumeInputEvents;
         }
@@ -784,20 +800,23 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         }
         break;
             
+    case Touch::TOUCH_MOVE:
+        break;
+
     case Touch::TOUCH_RELEASE:
         if (_contactIndex == (int)contactIndex)
         {
             _contactIndex = INVALID_CONTACT_INDEX;
 
-            // Always trigger Listener::RELEASE
-            notifyListeners(Listener::RELEASE);
+            // Always trigger Control::Listener::RELEASE
+            notifyListeners(Control::Listener::RELEASE);
 
-            // Only trigger Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
+            // Only trigger Control::Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
             if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
                 // Leave this control in the FOCUS state.
-                notifyListeners(Listener::CLICK);
+                notifyListeners(Control::Listener::CLICK);
             }
 
             return _consumeInputEvents;
@@ -825,6 +844,25 @@ bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
         return touchEvent(Touch::TOUCH_RELEASE, x, y, 0);
 
     case Mouse::MOUSE_MOVE:
+        if (_state != ACTIVE)
+        {
+            if (_state != HOVER &&
+                x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
+            {
+                _previousState = _state;
+                setState(HOVER);
+                notifyListeners(Control::Listener::ENTER);
+                return _consumeInputEvents;
+            }
+            else if (_state == HOVER && !(x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                        y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height))
+            {
+                setState(_previousState);
+                notifyListeners(Control::Listener::LEAVE);
+                return _consumeInputEvents;
+            }
+        }
         return touchEvent(Touch::TOUCH_MOVE, x, y, 0);
 
     default:
@@ -834,7 +872,42 @@ bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
     return false;
 }
 
-void Control::notifyListeners(Listener::EventType eventType)
+bool Control::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    // Default behavior for gamepad events.
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::FOCUS)
+        {
+            if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+                gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::PRESS);
+                return _consumeInputEvents;
+            }
+        }
+        else if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::RELEASE);
+                notifyListeners(Control::Listener::CLICK);
+                return _consumeInputEvents;
+            }
+        }
+        break;
+    case Gamepad::JOYSTICK_EVENT:
+        break;
+    case Gamepad::TRIGGER_EVENT:
+        break;
+    }
+
+    return false;
+}
+
+void Control::notifyListeners(Control::Listener::EventType eventType)
 {
     // This method runs untrusted code by notifying listeners of events.
     // If the user calls exit() or otherwise releases this control, we
@@ -843,11 +916,11 @@ void Control::notifyListeners(Listener::EventType eventType)
 
     if (_listeners)
     {
-        std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
+        std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->find(eventType);
         if (itr != _listeners->end())
         {
-            std::list<Listener*>* listenerList = itr->second;
-            for (std::list<Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); ++listenerItr)
+            std::list<Control::Listener*>* listenerList = itr->second;
+            for (std::list<Control::Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); ++listenerItr)
             {
                 GP_ASSERT(*listenerItr);
                 (*listenerItr)->controlEvent(this, eventType);
@@ -951,9 +1024,9 @@ void Control::update(const Control* container, const Vector2& offset)
  
     _viewportClipBounds.set(x, y, width, height);
 
-    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
-        width + border.left + padding.left + border.right + padding.right,
-        height + border.top + padding.top + border.bottom + padding.bottom);
+    width += border.left + padding.left + border.right + padding.right;
+    height += border.top + padding.top + border.bottom + padding.bottom;
+    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top, max(width, 0.0f), max(height, 0.0f));
     if (_clearBounds.isEmpty())
     {
         _clearBounds.set(_absoluteClipBounds);
@@ -1011,9 +1084,11 @@ void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
             spriteBatch->draw(rightX, _absoluteBounds.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
         if (border.left)
             spriteBatch->draw(_absoluteBounds.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
-        if (border.left && border.right && border.top && border.bottom)
-            spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
-                center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+
+        // Always draw the background.
+        spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
+            center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+
         if (border.right)
             spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
         if (border.bottom && border.left)
@@ -1088,6 +1163,10 @@ Control::State Control::getState(const char* state)
     {
         return DISABLED;
     }
+    else if (strcmp(state, "HOVER") == 0)
+    {
+        return HOVER;
+    }
 
     return NORMAL;
 }
@@ -1230,6 +1309,11 @@ Theme::Style::Overlay** Control::getOverlays(unsigned char overlayTypes, Theme::
         overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
     }
 
+    if ((overlayTypes & HOVER) == HOVER)
+    {
+        overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_HOVER);
+    }
+
     return overlays;
 }
 
@@ -1244,9 +1328,23 @@ Theme::Style::Overlay* Control::getOverlay(State state) const
     case Control::FOCUS:
         return _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
     case Control::ACTIVE:
-        return _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+    {
+        Theme::Style::Overlay* activeOverlay = _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+        if (activeOverlay)
+            return activeOverlay;
+        else
+            return getOverlay(_previousState);
+    }
     case Control::DISABLED:
         return _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
+    case Control::HOVER:
+    {
+        Theme::Style::Overlay* hoverOverlay = _style->getOverlay(Theme::Style::OVERLAY_HOVER);
+        if (hoverOverlay)
+            return hoverOverlay;
+        else
+            return getOverlay(_previousState);
+    }
     default:
         return NULL;
     }

+ 60 - 17
gameplay/src/Control.h

@@ -11,6 +11,7 @@
 #include "Keyboard.h"
 #include "Mouse.h"
 #include "ScriptTarget.h"
+#include "Gamepad.h"
 
 namespace gameplay
 {
@@ -56,6 +57,11 @@ public:
          * State of a control that has been disabled.
          */
         DISABLED = 0x08,
+
+        /**
+         * When a mouse is in use, the state of a control the cursor is over.
+         */
+        HOVER = 0x10,
     };
 
     /**
@@ -85,12 +91,6 @@ public:
         ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT
     };
 
-    /**
-     * @script{ignore}
-     * A constant used for setting themed attributes on all control states simultaneously.
-     */
-    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
-
     /**
      * Implement Control::Listener and call Control::addListener()
      * in order to listen for events on controls.
@@ -140,6 +140,16 @@ public:
              * Event triggered when a control is clicked with the right mouse button.
              */
             RIGHT_CLICK     = 0x40,
+
+            /**
+             * Event triggered when a mouse cursor enters a control.
+             */
+            ENTER           = 0x80,
+
+            /**
+             * Event triggered when a mouse cursor leaves a control.
+             */
+            LEAVE           = 0x100,
         };
     
         /*
@@ -156,6 +166,12 @@ public:
         virtual void controlEvent(Control* control, EventType evt) = 0;
     };
 
+    /**
+     * @script{ignore}
+     * A constant used for setting themed attributes on all control states simultaneously.
+     */
+    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED | HOVER;
+
     /**
      * Position animation property. Data = x, y
      */
@@ -244,6 +260,14 @@ public:
      */
     const Rectangle& getBounds() const;
 
+    /**
+     * Get the absolute bounds of this control, in pixels, including border and padding,
+     * before clipping.
+     *
+     * @return The absolute bounds of this control.
+     */
+    const Rectangle& getAbsoluteBounds() const;
+
     /**
      * Get the x coordinate of this control's bounds.
      *
@@ -841,6 +865,15 @@ protected:
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @param gamepad The gamepad whose state changed.
+     * @param evt The gamepad event that occurred.
+     * @param analogIndex If evt is JOYSTICK_EVENT or TRIGGER_EVENT, this will be the index of the corresponding control.
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
@@ -850,6 +883,14 @@ protected:
      */
     virtual void update(const Control* container, const Vector2& offset);
 
+    /**
+     * Draws the themed border and background of a control.
+     *
+     * @param spriteBatch The sprite batch containing this control's border images.
+     * @param clip The clipping rectangle of this control's parent container.
+     */
+    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+
     /**
      * Draw the images associated with this control.
      *
@@ -915,7 +956,7 @@ protected:
      *
      * @param eventType The event to trigger.
      */
-    void notifyListeners(Listener::EventType eventType);
+    void notifyListeners(Control::Listener::EventType eventType);
 
     /**
      * Gets the Alignment by string.
@@ -925,6 +966,14 @@ protected:
      */
     static Alignment getAlignment(const char* alignment);
 
+    /**
+     * Gets whether this control is in focus.
+     * Note that a control's state can be HOVER while the control is in focus.
+     * When the cursor leaves the control, it will return to the FOCUS state.
+     * This method will still return true in this case.
+     */
+    bool hasFocus() const;
+
     /** 
      * The Control's ID.
      */ 
@@ -1003,7 +1052,8 @@ protected:
     /**
      * Listeners map of EventType's to a list of Listeners.
      */
-    std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
+    //std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
+    std::map<Control::Listener::EventType, std::list<Control::Listener*>*>* _listeners;
     
     /**
      * The Control's Theme::Style.
@@ -1063,18 +1113,11 @@ private:
 
     Theme::Skin* getSkin(State state);
 
-    void addSpecificListener(Control::Listener* listener, Listener::EventType eventType);
-    
-    /**
-     * Draws the themed border and background of a control.
-     *
-     * @param spriteBatch The sprite batch containing this control's border images.
-     * @param clip The clipping rectangle of this control's parent container.
-     */
-    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void addSpecificListener(Control::Listener* listener, Control::Listener::EventType eventType);
     
     bool _styleOverridden;
     Theme::Skin* _skin;
+    State _previousState;
 };
 
 }

+ 79 - 18
gameplay/src/Curve.cpp

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

+ 34 - 2
gameplay/src/Curve.h

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

+ 115 - 27
gameplay/src/FileSystem.cpp

@@ -10,9 +10,12 @@
     #include <windows.h>
     #include <tchar.h>
     #include <stdio.h>
+    #include <direct.h>
     #define gp_stat _stat
     #define gp_stat_struct struct stat
 #else
+    #define __EXT_POSIX2
+    #include <libgen.h>
     #include <dirent.h>
     #define gp_stat stat
     #define gp_stat_struct struct stat
@@ -86,6 +89,27 @@ static bool androidFileExists(const char* filePath)
 static std::string __resourcePath("./");
 static std::map<std::string, std::string> __aliases;
 
+/**
+ * Gets the fully resolved path.
+ * If the path is relative then it will be prefixed with the resource path.
+ * Aliases will be converted to a relative path.
+ * 
+ * @param path The path to resolve.
+ * @param fullPath The full resolved path. (out param)
+ */
+static void getFullPath(const char* path, std::string& fullPath)
+{
+    if (FileSystem::isAbsolutePath(path))
+    {
+        fullPath.assign(path);
+    }
+    else
+    {
+        fullPath.assign(__resourcePath);
+        fullPath += FileSystem::resolvePath(path);
+    }
+}
+
 /**
  * 
  * @script{ignore}
@@ -259,30 +283,54 @@ bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
         path.append(dirPath);
     }
     path.append("/.");
+    bool result = false;
+
     struct dirent* dp;
     DIR* dir = opendir(path.c_str());
-    if (!dir)
+    if (dir != NULL)
     {
-        return false;
+        while ((dp = readdir(dir)) != NULL)
+        {
+            std::string filepath(path);
+            filepath.append("/");
+            filepath.append(dp->d_name);
+
+            struct stat buf;
+            if (!stat(filepath.c_str(), &buf))
+            {
+                // Add to the list if this is not a directory
+                if (!S_ISDIR(buf.st_mode))
+                {
+                    files.push_back(dp->d_name);
+                }
+            }
+        }
+        closedir(dir);
+        result = true;
     }
-    while ((dp = readdir(dir)) != NULL)
-    {
-        std::string filepath(path);
-        filepath.append("/");
-        filepath.append(dp->d_name);
 
-        struct stat buf;
-        if (!stat(filepath.c_str(), &buf))
+#ifdef __ANDROID__
+    // List the files that are in the android APK at this path
+    AAssetDir* assetDir = AAssetManager_openDir(__assetManager, dirPath);
+    if (assetDir != NULL)
+    {
+        AAssetDir_rewind(assetDir);
+        const char* file = NULL;
+        while ((file = AAssetDir_getNextFileName(assetDir)) != NULL)
         {
-            // Add to the list if this is not a directory
-            if (!S_ISDIR(buf.st_mode))
+            std::string filename(file);
+            // Check if this file was already added to the list because it was copied to the SD card.
+            if (find(files.begin(), files.end(), filename) == files.end())
             {
-                files.push_back(dp->d_name);
+                files.push_back(filename);
             }
         }
+        AAssetDir_close(assetDir);
+        result = true;
     }
-    closedir(dir);
-    return true;
+#endif
+
+    return result;
 #endif
 }
 
@@ -291,19 +339,19 @@ bool FileSystem::fileExists(const char* filePath)
     GP_ASSERT(filePath);
 
 #ifdef __ANDROID__
-    if (androidFileExists(filePath))
+    if (androidFileExists(resolvePath(filePath)))
     {
         return true;
     }
 #endif
 
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(filePath);
+    std::string fullPath;
+    getFullPath(filePath, fullPath);
 
     gp_stat_struct s;
 
 #ifdef WIN32
-    if (stat(fullPath.c_str(), &s) != 0)
+    if (!isAbsolutePath(filePath) && stat(fullPath.c_str(), &s) != 0)
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
@@ -352,12 +400,12 @@ Stream* FileSystem::open(const char* path, size_t mode)
         return FileStreamAndroid::create(resolvePath(path), modeStr);
     }
 #else
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(path);
+    std::string fullPath;
+    getFullPath(path, fullPath);
     
 #ifdef WIN32
     gp_stat_struct s;
-    if (stat(fullPath.c_str(), &s) != 0 && (mode & WRITE) == 0)
+    if (!isAbsolutePath(path) && stat(fullPath.c_str(), &s) != 0 && (mode & WRITE) == 0)
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
@@ -386,15 +434,15 @@ FILE* FileSystem::openFile(const char* filePath, const char* mode)
     GP_ASSERT(filePath);
     GP_ASSERT(mode);
 
-    std::string fullPath(__resourcePath);
-    fullPath += resolvePath(filePath);
+    std::string fullPath;
+    getFullPath(filePath, fullPath);
 
     createFileFromAsset(filePath);
     
     FILE* fp = fopen(fullPath.c_str(), mode);
     
 #ifdef WIN32
-    if (fp == NULL)
+    if (fp == NULL && !isAbsolutePath(filePath))
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
@@ -452,11 +500,10 @@ bool FileSystem::isAbsolutePath(const char* filePath)
     if (filePath == 0 || filePath[0] == '\0')
         return false;
 #ifdef WIN32
-    if (strlen(filePath) >= 2)
+    if (filePath[1] != '\0')
     {
         char first = filePath[0];
-        if (filePath[1] == ':' && ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')))
-            return true;
+        return (filePath[1] == ':' && ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')));
     }
     return false;
 #else
@@ -516,6 +563,47 @@ void FileSystem::createFileFromAsset(const char* path)
 #endif
 }
 
+std::string FileSystem::getDirectoryName(const char* path)
+{
+    if (path == NULL || strlen(path) == 0)
+    {
+        return "";
+    }
+#ifdef WIN32
+    char drive[_MAX_DRIVE];
+    char dir[_MAX_DIR];
+    _splitpath(path, drive, dir, NULL, NULL);
+    std::string dirname;
+    size_t driveLength = strlen(drive);
+    if (driveLength > 0)
+    {
+        dirname.reserve(driveLength + strlen(dir));
+        dirname.append(drive);
+        dirname.append(dir);
+    }
+    else
+    {
+        dirname.assign(dir);
+    }
+    std::replace(dirname.begin(), dirname.end(), '\\', '/');
+    return dirname;
+#else
+    // dirname() modifies the input string so create a temp string
+    std::string dirname;
+    char* tempPath = new char[strlen(path) + 1];
+    strcpy(tempPath, path);
+    char* dir = ::dirname(tempPath);
+    if (dir && strlen(dir) > 0)
+    {
+        dirname.assign(dir);
+        // dirname() strips off the trailing '/' so add it back to be consistent with Windows
+        dirname.append("/");
+    }
+    SAFE_DELETE_ARRAY(tempPath);
+    return dirname;
+#endif
+}
+
 std::string FileSystem::getExtension(const char* path)
 {
     const char* str = strrchr(path, '.');

+ 17 - 0
gameplay/src/FileSystem.h

@@ -2,6 +2,7 @@
 #define FILESYSTEM_H_
 
 #include "Stream.h"
+#include <string>
 
 namespace gameplay
 {
@@ -181,6 +182,22 @@ public:
      */
     static void createFileFromAsset(const char* path);
 
+    /**
+     * Returns the directory name up to and including the trailing '/'.
+     * 
+     * This is a lexical method so it does not verify that the directory exists.
+     * Back slashes will be converted to forward slashes.
+     * 
+     * - "res/image.png" will return "res/"
+     * - "image.png" will return ""
+     * - "c:\foo\bar\image.png" will return "c:/foo/bar/"
+     * 
+     * @param The file path. May be relative or absolute, forward or back slashes. May be NULL.
+     * 
+     * @return The directory name with the trailing '/'. Returns "" if path is NULL or the path does not contain a directory.
+     */
+    static std::string getDirectoryName(const char* path);
+
     /**
      * Returns the extension of the given file path.
      *

+ 192 - 159
gameplay/src/Form.cpp

@@ -20,7 +20,8 @@ namespace gameplay
 static Effect* __formEffect = NULL;
 static std::vector<Form*> __forms;
 
-Form::Form() : _theme(NULL), _frameBuffer(NULL), _spriteBatch(NULL), _node(NULL), _nodeQuad(NULL), _nodeMaterial(NULL) , _u2(0), _v1(0)
+Form::Form() : _theme(NULL), _frameBuffer(NULL), _spriteBatch(NULL), _node(NULL),
+    _nodeQuad(NULL), _nodeMaterial(NULL) , _u2(0), _v1(0), _isGamepad(false)
 {
 }
 
@@ -151,7 +152,7 @@ Form* Form::create(const char* url)
     }
     form->initialize(style, formProperties);
 
-    form->_consumeInputEvents = formProperties->getBool("consumeInputEvents", true);
+    form->_consumeInputEvents = formProperties->getBool("consumeInputEvents", false);
 
     // Alignment
     if ((form->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
@@ -389,138 +390,138 @@ void Form::setNode(Node* node)
 }
 
 void Form::update(float elapsedTime)
-{
-    updateBounds();
-}
-
-void Form::updateBounds()
 {
     if (isDirty())
     {
-        _clearBounds.set(_absoluteClipBounds);
-
-        // Calculate the clipped bounds.
-        float x = 0;
-        float y = 0;
-        float width = _bounds.width;
-        float height = _bounds.height;
-
-        Rectangle clip(0, 0, _bounds.width, _bounds.height);
+        updateBounds();
 
-        float clipX2 = clip.x + clip.width;
-        float x2 = clip.x + x + width;
-        if (x2 > clipX2)
-            width -= x2 - clipX2;
-
-        float clipY2 = clip.y + clip.height;
-        float y2 = clip.y + y + height;
-        if (y2 > clipY2)
-            height -= y2 - clipY2;
+        // Cache themed attributes for performance.
+        _skin = getSkin(_state);
+        _opacity = getOpacity(_state);
 
-        if (x < 0)
+        GP_ASSERT(_layout);
+        if (_scroll != SCROLL_NONE)
         {
-            width += x;
-            x = -x;
+            updateScroll();
         }
         else
         {
-            x = 0;
+            _layout->update(this, Vector2::zero());
         }
+    }
+}
 
-        if (y < 0)
-        {
-            height += y;
-            y = -y;
-        }
-        else
-        {
-            y = 0;
-        }
+void Form::updateBounds()
+{   
+    _clearBounds.set(_absoluteClipBounds);
+
+    // Calculate the clipped bounds.
+    float x = 0;
+    float y = 0;
+    float width = _bounds.width;
+    float height = _bounds.height;
 
-        _clipBounds.set(x, y, width, height);
+    Rectangle clip(0, 0, _bounds.width, _bounds.height);
 
-        // Calculate the absolute bounds.
+    float clipX2 = clip.x + clip.width;
+    float x2 = clip.x + x + width;
+    if (x2 > clipX2)
+        width -= x2 - clipX2;
+
+    float clipY2 = clip.y + clip.height;
+    float y2 = clip.y + y + height;
+    if (y2 > clipY2)
+        height -= y2 - clipY2;
+
+    if (x < 0)
+    {
+        width += x;
+        x = -x;
+    }
+    else
+    {
         x = 0;
+    }
+
+    if (y < 0)
+    {
+        height += y;
+        y = -y;
+    }
+    else
+    {
         y = 0;
-        _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
+    }
 
-        // Calculate the absolute viewport bounds. Absolute bounds minus border and padding.
-        const Theme::Border& border = getBorder(_state);
-        const Theme::Padding& padding = getPadding();
+    _clipBounds.set(x, y, width, height);
 
-        x += border.left + padding.left;
-        y += border.top + padding.top;
-        width = _bounds.width - border.left - padding.left - border.right - padding.right;
-        height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
+    // Calculate the absolute bounds.
+    x = 0;
+    y = 0;
+    _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
 
-        _viewportBounds.set(x, y, width, height);
+    // Calculate the absolute viewport bounds. Absolute bounds minus border and padding.
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
 
-        // Calculate the clip area. Absolute bounds, minus border and padding, clipped to the parent container's clip area.
-        clipX2 = clip.x + clip.width;
-        x2 = x + width;
-        if (x2 > clipX2)
-            width = clipX2 - x;
+    x += border.left + padding.left;
+    y += border.top + padding.top;
+    width = _bounds.width - border.left - padding.left - border.right - padding.right;
+    height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
 
-        clipY2 = clip.y + clip.height;
-        y2 = y + height;
-        if (y2 > clipY2)
-            height = clipY2 - y;
+    _viewportBounds.set(x, y, width, height);
 
-        if (x < clip.x)
-        {
-            float dx = clip.x - x;
-            width -= dx;
-            x = clip.x;
-        }
+    // Calculate the clip area. Absolute bounds, minus border and padding, clipped to the parent container's clip area.
+    clipX2 = clip.x + clip.width;
+    x2 = x + width;
+    if (x2 > clipX2)
+        width = clipX2 - x;
 
-        if (y < clip.y)
-        {
-            float dy = clip.y - y;
-            height -= dy;
-            y = clip.y;
-        }
- 
-        _viewportClipBounds.set(x, y, width, height);
-        _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
-                                width + border.left + padding.left + border.right + padding.right,
-                                height + border.top + padding.top + border.bottom + padding.bottom);
-        if (_clearBounds.isEmpty())
-        {
-            _clearBounds.set(_absoluteClipBounds);
-        }
+    clipY2 = clip.y + clip.height;
+    y2 = y + height;
+    if (y2 > clipY2)
+        height = clipY2 - y;
 
-        // Cache themed attributes for performance.
-        _skin = getSkin(_state);
-        _opacity = getOpacity(_state);
+    if (x < clip.x)
+    {
+        float dx = clip.x - x;
+        width -= dx;
+        x = clip.x;
+    }
 
-        // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
-        if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
-        {
-            _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
-            _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
-            _scrollBarRightCap = getImage("scrollBarRightCap", _state);
+    if (y < clip.y)
+    {
+        float dy = clip.y - y;
+        height -= dy;
+        y = clip.y;
+    }
+ 
+    _viewportClipBounds.set(x, y, width, height);
+    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
+                            width + border.left + padding.left + border.right + padding.right,
+                            height + border.top + padding.top + border.bottom + padding.bottom);
+    if (_clearBounds.isEmpty())
+    {
+        _clearBounds.set(_absoluteClipBounds);
+    }
 
-            _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
-        }
+    // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+    if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
+    {
+        _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
+        _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
+        _scrollBarRightCap = getImage("scrollBarRightCap", _state);
 
-        if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
-        {
-            _scrollBarTopCap = getImage("scrollBarTopCap", _state);
-            _scrollBarVertical = getImage("verticalScrollBar", _state);
-            _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
-        
-            _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
-        }
+        _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
+    }
 
-        GP_ASSERT(_layout);
-        if (_scroll != SCROLL_NONE)
-        {
-            updateScroll();
-        }
-        else
-        {
-            _layout->update(this, Vector2::zero());
-        }
+    if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+    {
+        _scrollBarTopCap = getImage("scrollBarTopCap", _state);
+        _scrollBarVertical = getImage("verticalScrollBar", _state);
+        _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+        
+        _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
 }
 
@@ -546,20 +547,25 @@ void Form::draw()
 
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
+        
+        // By setting needsClear to true here, an optimization meant to clear and redraw only areas of the form
+        // that have changed is disabled.  Currently, repositioning controls can result in areas of the screen being cleared
+        // after another control has been drawn there.  This should probably be done in two passes -- one to clear areas where
+        // dirty controls were last frame, and another to draw them where they are now.
         Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height),
-                        _skin != NULL, false, _bounds.height);
+                        /*_skin != NULL*/ true, false, _bounds.height);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
-        // restore the previous game viewport
+        // Restore the previous game viewport.
         game->setViewport(prevViewport);
         // Rebind the previous framebuffer and game viewport.
         previousFrameBuffer->bind();
     }
 
-    // Draw either with a 3D quad or sprite batch
+    // Draw either with a 3D quad or sprite batch.
     if (_node)
     {
-         // If we have the node set, then draw a 3D quad model
+         // If we have the node set, then draw a 3D quad model.
         _nodeQuad->draw();
     }
     else
@@ -581,11 +587,35 @@ const char* Form::getType() const
     return "form";
 }
 
+void Form::updateInternal(float elapsedTime)
+{
+    size_t size = __forms.size();
+    for (size_t i = 0; i < size; ++i)
+    {
+        Form* form = __forms[i];
+        GP_ASSERT(form);
+
+        if (form->isEnabled() && form->isVisible())
+        {
+            form->update(elapsedTime);
+        }
+    }
+}
+
+static bool shouldPropagateTouchEvent(Control::State state, Touch::TouchEvent evt, const Rectangle& bounds, int x, int y)
+{
+    return (state != Control::NORMAL ||
+            (evt == Touch::TOUCH_PRESS &&
+             x >= bounds.x &&
+             x <= bounds.x + bounds.width &&
+             y >= bounds.y &&
+             y <= bounds.y + bounds.height));
+}
+
 bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     // Check for a collision with each Form in __forms.
     // Pass the event on.
-    bool eventConsumed = false;
     size_t size = __forms.size();
     for (size_t i = 0; i < size; ++i)
     {
@@ -600,14 +630,10 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
                 if (form->projectPoint(x, y, &point))
                 {
                     const Rectangle& bounds = form->getBounds();
-                    if (form->getState() == Control::FOCUS ||
-                        (evt == Touch::TOUCH_PRESS &&
-                         point.x >= bounds.x &&
-                         point.x <= bounds.x + bounds.width &&
-                         point.y >= bounds.y &&
-                         point.y <= bounds.y + bounds.height))
+                    if (shouldPropagateTouchEvent(form->getState(), evt, bounds, point.x, point.y))
                     {
-                        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;
                     }
                 }
             }
@@ -615,20 +641,16 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
             {
                 // Simply compare with the form's bounds.
                 const Rectangle& bounds = form->getBounds();
-                if (form->getState() == Control::FOCUS ||
-                    (evt == Touch::TOUCH_PRESS &&
-                        x >= bounds.x &&
-                        x <= bounds.x + bounds.width &&
-                        y >= bounds.y &&
-                        y <= bounds.y + bounds.height))
+                if (shouldPropagateTouchEvent(form->getState(), evt, bounds, x, y))
                 {
                     // Pass on the event's position relative to the form.
-                    eventConsumed |= form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex);
+                    if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
+                        return true;
                 }
             }
         }
     }
-    return eventConsumed;
+    return false;
 }
 
 bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
@@ -638,7 +660,7 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     {
         Form* form = __forms[i];
         GP_ASSERT(form);
-        if (form->isEnabled() && form->isVisible())
+        if (form->isEnabled() && form->isVisible() && form->hasFocus() && !form->_isGamepad)
         {
             if (form->keyEvent(evt, key))
                 return true;
@@ -647,10 +669,22 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     return false;
 }
 
-bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+static bool shouldPropagateMouseEvent(Control::State state, Mouse::MouseEvent evt, const Rectangle& bounds, int x, int y)
 {
-    bool eventConsumed = false;
+    return (state != Control::NORMAL ||
+            ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
+              evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
+              evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+              evt == Mouse::MOUSE_MOVE ||
+              evt == Mouse::MOUSE_WHEEL) &&
+                x >= bounds.x &&
+                x <= bounds.x + bounds.width &&
+                y >= bounds.y &&
+                y <= bounds.y + bounds.height));
+}
 
+bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
     for (size_t i = 0; i < __forms.size(); ++i)
     {
         Form* form = __forms[i];
@@ -664,17 +698,10 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
                 if (form->projectPoint(x, y, &point))
                 {
                     const Rectangle& bounds = form->getBounds();
-                    if (form->getState() == Control::FOCUS ||
-                        ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
-                          evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
-                          evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
-                          evt == Mouse::MOUSE_WHEEL) &&
-                         point.x >= bounds.x &&
-                         point.x <= bounds.x + bounds.width &&
-                         point.y >= bounds.y &&
-                         point.y <= bounds.y + bounds.height))
+                    if (shouldPropagateMouseEvent(form->getState(), evt, bounds, point.x, point.y))
                     {
-                        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;
                     }
                 }
             }
@@ -682,23 +709,31 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
             {
                 // Simply compare with the form's bounds.
                 const Rectangle& bounds = form->getBounds();
-                if (form->getState() == Control::FOCUS ||
-                    ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
-                      evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
-                      evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
-                      evt == Mouse::MOUSE_WHEEL) &&
-                        x >= bounds.x &&
-                        x <= bounds.x + bounds.width &&
-                        y >= bounds.y &&
-                        y <= bounds.y + bounds.height))
+                if (shouldPropagateMouseEvent(form->getState(), evt, bounds, x, y))
                 {
                     // 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;
+}
+
+void 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->hasFocus())
+        {
+            if (form->gamepadEvent(evt, gamepad, analogIndex))
+                return;
+        }
+    }
 }
 
 bool Form::projectPoint(int x, int y, Vector3* point)
@@ -710,21 +745,19 @@ bool Form::projectPoint(int x, int y, Vector3* point)
     {
         // Get info about the form's position.
         Matrix m = _node->getWorldMatrix();
-        Vector3 min(0, 0, 0);
-        m.transformPoint(&min);
+        Vector3 pointOnPlane(0, 0, 0);
+        m.transformPoint(&pointOnPlane);
 
         // Unproject point into world space.
         Ray ray;
         camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
 
         // Find the quad's plane.  We know its normal is the quad's forward vector.
-        Vector3 normal = _node->getForwardVectorWorld();
+        Vector3 normal = _node->getForwardVectorWorld().normalize();
 
-        // To get the plane's distance from the origin, we'll find the distance from the plane defined
-        // by the quad's forward vector and one of its points to the plane defined by the same vector and the origin.
-        const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
-        const float d = -(a*min.x) - (b*min.y) - (c*min.z);
-        const float distance = fabs(d) /  sqrt(a*a + b*b + c*c);
+        // To get the plane's distance from the origin, we project a point on the
+        // plane onto the plane's normal vector.
+        const float distance = fabs(Vector3::dot(pointOnPlane, normal));
         Plane plane(normal, -distance);
 
         // Check for collision with plane.

+ 17 - 1
gameplay/src/Form.h

@@ -9,6 +9,7 @@
 #include "Touch.h"
 #include "Keyboard.h"
 #include "Mouse.h"
+#include "Gamepad.h"
 
 namespace gameplay
 {
@@ -48,6 +49,8 @@ class Theme;
 class Form : public Container
 {
     friend class Platform;
+    friend class Game;
+    friend class Gamepad;
 
 public:
 
@@ -188,6 +191,11 @@ private:
      */
     void updateBounds();
 
+    /**
+     * Updates all visible, enabled forms.
+     */
+    static void updateInternal(float elapsedTime);
+
     /**
      * Propagate touch events to enabled forms.
      *
@@ -211,6 +219,13 @@ private:
      */
     static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Propagate gamepad events to enabled forms.
+     *
+     * @see Control::gamepadEvent
+     */
+    static void gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
      * Get the next highest power of two of an integer.  Used when creating framebuffers.
      *
@@ -240,7 +255,8 @@ private:
     float _u2;
     float _v1;
     Matrix _projectionMatrix;           // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
-    Matrix _defaultProjectionMatrix;   
+    Matrix _defaultProjectionMatrix;
+    bool _isGamepad;
 };
 
 }

+ 23 - 2
gameplay/src/FrameBuffer.cpp

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

+ 22 - 0
gameplay/src/FrameBuffer.h

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

+ 16 - 4
gameplay/src/Frustum.cpp

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

+ 21 - 1
gameplay/src/Frustum.h

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

+ 55 - 20
gameplay/src/Game.cpp

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

+ 63 - 6
gameplay/src/Game.h

@@ -316,6 +316,17 @@ public:
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
     
+    /**
+     * Called when the game window has been resized.
+     *
+     * This method is called once the game window is created with its initial size
+     * and then again any time the game window changes size.
+     *
+     * @param width The new game window width.
+     * @param height The new game window height.
+     */
+    virtual void resizeEvent(unsigned int width, unsigned int height);
+
     /** 
      * Gets whether the current platform supports mouse input.
      *
@@ -432,11 +443,11 @@ public:
     virtual void gestureTapEvent(int x, int y);
 
     /**
-     * Gamepad callback on gamepad events. Override to receive Gamepad::CONNECTED_EVENT 
-     * and Gamepad::DISCONNECTED_EVENT.
+     * Gamepad callback on gamepad events.  Override to receive Gamepad::CONNECTED_EVENT 
+     * and Gamepad::DISCONNECTED_EVENT, and store the Gamepad* in order to poll it from update().
      *
      * @param evt The gamepad event that occurred.
-     * @param gamepad the gamepad the event occurred on
+     * @param gamepad The gamepad that generated the event.
      */
     virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
 
@@ -464,6 +475,20 @@ public:
      */
     inline Gamepad* getGamepad(unsigned int index, bool preferPhysical = true) const;
 
+    /**
+	 * Sets whether multi-sampling is to be enabled/disabled. Default is disabled.
+	 *
+	 * @param enabled true sets multi-sampling to be enabled, false to be disabled.
+	 */
+	inline void setMultiSampling(bool enabled);
+
+	/*
+	 * Is multi-sampling enabled.
+	 *
+	 * @return true if multi-sampling is enabled.
+	 */
+	inline bool isMultiSampling() const;
+
     /**
      * Sets multi-touch is to be enabled/disabled. Default is disabled.
      *
@@ -486,13 +511,45 @@ public:
     inline bool canExit() const;
 
     /**
-     * Gets the current accelerometer values.
+     * Whether this game has accelerometer support.
+     */
+    inline bool hasAccelerometer() const;
+
+    /**
+     * Gets the current accelerometer values for use as an indication of device
+     * orientation. Despite its name, implementations are at liberty to combine
+     * accelerometer data with data from other sensors as well, such as the gyros.
+     * This method is best used to obtain an indication of device orientation; it
+     * does not necessarily distinguish between acceleration and rotation rate.
      *
-     * @param pitch The pitch angle returned (in degrees). If NULL then not returned.
-     * @param roll The roll angle returned (in degrees). If NULL then not returned.
+     * @param pitch The pitch angle returned (in degrees). Zero if hasAccelerometer() returns false.
+     * @param roll The roll angle returned (in degrees). Zero if hasAccelerometer() returns false.
      */
     inline void getAccelerometerValues(float* pitch, float* roll);
 
+    /**
+     * Gets raw sensor values, if equipped, allowing a distinction between device acceleration
+     * and rotation rate. Returns zeros on platforms with no corresponding support. See also
+     * hasAccelerometer() and getAccelerometerValues().
+     *
+     * @param accelX The x-coordinate of the raw accelerometer data.
+     * @param accelY The y-coordinate of the raw accelerometer data.
+     * @param accelZ The z-coordinate of the raw accelerometer data.
+     * @param gyroX The x-coordinate of the raw gyroscope data.
+     * @param gyroY The y-coordinate of the raw gyroscope data.
+     * @param gyroZ The z-coordinate of the raw gyroscope data.
+     */
+    inline void getRawSensorValues(float* accelX, float* accelY, float* accelZ, float* gyroX, float* gyroY, float* gyroZ);
+
+    /**
+     * Gets the command line arguments.
+     * 
+     * @param argc The number of command line arguments.
+     * @param argv The array of command line arguments.
+     * @script{ignore}
+     */
+    void getArguments(int* argc, char*** argv) const;
+
     /**
      * Schedules a time event to be sent to the given TimeListener a given number of game milliseconds from now.
      * Game time stops while the game is paused. A time offset of zero will fire the time event in the next frame.

+ 20 - 0
gameplay/src/Game.inl

@@ -96,6 +96,16 @@ inline bool Game::isCursorVisible()
     return Platform::isCursorVisible();
 }
 
+inline void Game::setMultiSampling(bool enabled)
+{
+    Platform::setMultiSampling(enabled);
+}
+
+inline bool Game::isMultiSampling() const
+{
+    return Platform::isMultiSampling();
+}
+
 inline void Game::setMultiTouch(bool enabled)
 {
     Platform::setMultiTouch(enabled);
@@ -111,11 +121,21 @@ inline bool Game::canExit() const
     return Platform::canExit();
 }
 
+inline bool Game::hasAccelerometer() const
+{
+	return Platform::hasAccelerometer();
+}
+
 inline void Game::getAccelerometerValues(float* pitch, float* roll)
 {
     Platform::getAccelerometerValues(pitch, roll);
 }
 
+inline void Game::getRawSensorValues(float* accelX, float* accelY, float* accelZ, float* gyroX, float* gyroY, float* gyroZ)
+{
+    return Platform::getRawSensorValues(accelX, accelY, accelZ, gyroX, gyroY, gyroZ);
+}
+
 inline unsigned int Game::getGamepadCount() const
 {
     return Gamepad::getGamepadCount();

+ 74 - 11
gameplay/src/Gamepad.cpp

@@ -3,6 +3,8 @@
 #include "Game.h"
 #include "Button.h"
 #include "Platform.h"
+#include "Form.h"
+#include "Joystick.h"
 
 namespace gameplay
 {
@@ -17,12 +19,14 @@ Gamepad::Gamepad(const char* formPath)
     _form = Form::create(formPath);
     GP_ASSERT(_form);
     _form->setConsumeInputEvents(false);
+    _form->_isGamepad = true;
     _vendorString = "None";
     _productString = "Virtual";
 
     for (int i = 0; i < 2; ++i)
     {
         _uiJoysticks[i] = NULL;
+        _triggers[i] = 0.0f;
     }
 
     for (int i = 0; i < 20; ++i)
@@ -36,9 +40,22 @@ Gamepad::Gamepad(const char* formPath)
 Gamepad::Gamepad(GamepadHandle handle, unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
                  unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
     : _handle(handle), _buttonCount(buttonCount), _joystickCount(joystickCount), _triggerCount(triggerCount),
-      _vendorId(vendorId), _productId(productId), _vendorString(vendorString), _productString(productString),
-      _form(NULL), _buttons(0)
+      _vendorId(vendorId), _productId(productId), _form(NULL), _buttons(0)
 {
+    if (vendorString)
+    {
+        _vendorString = vendorString;
+    }
+    
+    if (productString)
+    {
+        _productString = productString;
+    }
+
+    for (int i = 0; i < 2; ++i)
+    {
+        _triggers[i] = 0.0f;
+    }
 }
 
 Gamepad::~Gamepad()
@@ -124,12 +141,14 @@ void Gamepad::bindGamepadControls(Container* container)
         else if (std::strcmp("joystick", control->getType()) == 0)
         {
             Joystick* joystick = (Joystick*)control;
+            joystick->setConsumeInputEvents(true);
             _uiJoysticks[joystick->getIndex()] = joystick;
             _joystickCount++;
         }
         else if (std::strcmp("button", control->getType()) == 0)
         {
             Button* button = (Button*)control;
+            button->setConsumeInputEvents(true);
             _uiButtons[button->getDataBinding()] = button;
             _buttonCount++;
         }
@@ -173,6 +192,19 @@ Gamepad* Gamepad::getGamepad(unsigned int index, bool preferPhysical)
     return backupVirtual;
 }
 
+Gamepad* Gamepad::getGamepad(GamepadHandle handle)
+{
+    unsigned int count = __gamepads.size();
+    for (unsigned int i = 0; i < count; ++i)
+    {
+        if (__gamepads[i]->_handle == handle)
+        {
+            return __gamepads[i];
+        }
+    }
+    return NULL;
+}
+
 Gamepad::ButtonMapping Gamepad::getButtonMappingFromString(const char* string)
 {
     if (strcmp(string, "A") == 0 || strcmp(string, "BUTTON_A") == 0)
@@ -242,16 +274,18 @@ const char* Gamepad::getProductString() const
 
 void Gamepad::update(float elapsedTime)
 {
-    if (_form)
+    if (!_form)
     {
-        if (_form->isEnabled())
-        {
-            _form->update(elapsedTime);
-        }
+        Platform::pollGamepadState(this);
     }
-    else
+}
+
+void Gamepad::updateInternal(float elapsedTime)
+{
+    unsigned int size = __gamepads.size();
+    for (unsigned int i = 0; i < size; ++i)
     {
-        Platform::pollGamepadState(this);
+        __gamepads[i]->update(elapsedTime);
     }
 }
 
@@ -297,7 +331,8 @@ unsigned int Gamepad::getJoystickCount() const
 
 void Gamepad::getJoystickValues(unsigned int joystickId, Vector2* outValue) const
 {
-    GP_ASSERT(joystickId < _joystickCount);
+    if (joystickId >= _joystickCount)
+        return;
 
     if (_form)
     {
@@ -325,7 +360,8 @@ unsigned int Gamepad::getTriggerCount() const
 
 float Gamepad::getTriggerValue(unsigned int triggerId) const
 {
-    GP_ASSERT(triggerId < _triggerCount);
+    if (triggerId >= _triggerCount)
+        return 0.0f;
 
     if (_form)
     {
@@ -348,4 +384,31 @@ Form* Gamepad::getForm() const
     return _form;
 }
 
+void Gamepad::setButtons(unsigned int buttons)
+{
+    if (buttons != _buttons)
+    {
+        _buttons = buttons;
+        Platform::gamepadEventInternal(BUTTON_EVENT, this);
+    }
+}
+
+void Gamepad::setJoystickValue(unsigned int index, float x, float y)
+{
+    if (_joysticks[index].x != x || _joysticks[index].y != y)
+    {
+        _joysticks[index].set(x, y);
+        Platform::gamepadEventInternal(JOYSTICK_EVENT, this, index);
+    }
+}
+
+void Gamepad::setTriggerValue(unsigned int index, float value)
+{
+    if (_triggers[index] != value)
+    {
+        _triggers[index] = value;
+        Platform::gamepadEventInternal(TRIGGER_EVENT, this, index);
+    }
+}
+
 }

+ 25 - 23
gameplay/src/Gamepad.h

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

+ 1 - 1
gameplay/src/Image.h

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

+ 148 - 0
gameplay/src/ImageControl.cpp

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

+ 146 - 0
gameplay/src/ImageControl.h

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

+ 89 - 3
gameplay/src/Joint.cpp

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

+ 26 - 7
gameplay/src/Joint.h

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

+ 8 - 11
gameplay/src/Joystick.cpp

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

+ 9 - 7
gameplay/src/Label.cpp

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

+ 85 - 0
gameplay/src/Light.cpp

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

+ 14 - 0
gameplay/src/Light.h

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

+ 3 - 3
gameplay/src/Material.cpp

@@ -412,8 +412,8 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
             }
 
             // Get the texture path.
-            const char* path = ns->getString("path");
-            if (path == NULL || strlen(path) == 0)
+            std::string path;
+            if (!ns->getPath("path", &path))
             {
                 GP_ERROR("Texture sampler '%s' is missing required image file path.", name);
                 continue;
@@ -428,7 +428,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
 
             // Set the sampler parameter.
             GP_ASSERT(renderState->getParameter(name));
-            Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path, mipmap);
+            Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path.c_str(), mipmap);
             if (sampler)
             {
                 sampler->setWrapMode(wrapS, wrapT);

+ 204 - 21
gameplay/src/MaterialParameter.cpp

@@ -225,47 +225,225 @@ void MaterialParameter::setValue(const Matrix* values, unsigned int count)
 
 void MaterialParameter::setValue(const Texture::Sampler* sampler)
 {
+    GP_ASSERT(sampler);
     clearValue();
 
+    const_cast<Texture::Sampler*>(sampler)->addRef();
+    _value.samplerValue = sampler;
+    _type = MaterialParameter::SAMPLER;
+}
+
+void MaterialParameter::setValue(const Texture::Sampler** samplers, unsigned int count)
+{
+    GP_ASSERT(samplers);
+    clearValue();
+
+    for (unsigned int i = 0; i < count; ++i)
+    {
+        const_cast<Texture::Sampler*>(samplers[i])->addRef();
+    }
+    _value.samplerArrayValue = samplers;
+    _count = count;
+    _type = MaterialParameter::SAMPLER_ARRAY;
+}
+
+Texture::Sampler* MaterialParameter::setValue(const char* texturePath, bool generateMipmaps)
+{
+    GP_ASSERT(texturePath);
+    clearValue();
+
+    Texture::Sampler* sampler = Texture::Sampler::create(texturePath, generateMipmaps);
     if (sampler)
     {
-        const_cast<Texture::Sampler*>(sampler)->addRef();
         _value.samplerValue = sampler;
         _type = MaterialParameter::SAMPLER;
     }
+    return sampler;
 }
 
-void MaterialParameter::setValue(const Texture::Sampler** samplers, unsigned int count)
+void MaterialParameter::setFloat(float value)
 {
+    setValue(value);
+}
+
+void MaterialParameter::setFloatArray(const float* values, unsigned int count, bool copy)
+{
+    GP_ASSERT(values);
     clearValue();
 
-    if (samplers)
+    if (copy)
     {
-        for (unsigned int i = 0; i < count; ++i)
-        {
-            const_cast<Texture::Sampler*>(samplers[i])->addRef();
-        }
-        _value.samplerArrayValue = samplers;
-        _count = count;
-        _type = MaterialParameter::SAMPLER_ARRAY;
+        _value.floatPtrValue = new float[count];
+        memcpy(_value.floatPtrValue, values, sizeof(float) * count);
+        _dynamic = true;
     }
+    else
+    {
+        _value.floatPtrValue = const_cast<float*> (values);
+    }
+
+    _count = count;
+    _type = MaterialParameter::FLOAT_ARRAY;
 }
 
-Texture::Sampler* MaterialParameter::setValue(const char* texturePath, bool generateMipmaps)
+void MaterialParameter::setInt(int value)
+{
+    setValue(value);
+}
+
+void MaterialParameter::setIntArray(const int* values, unsigned int count, bool copy)
 {
-    if (texturePath)
+    GP_ASSERT(values);
+    clearValue();
+
+    if (copy)
     {
-        clearValue();
+        _value.intPtrValue = new int[count];
+        memcpy(_value.intPtrValue, values, sizeof(int) * count);
+        _dynamic = true;
+    }
+    else
+    {
+        _value.intPtrValue = const_cast<int*> (values);
+    }
 
-        Texture::Sampler* sampler = Texture::Sampler::create(texturePath, generateMipmaps);
-        if (sampler)
-        {
-            _value.samplerValue = sampler;
-            _type = MaterialParameter::SAMPLER;
-        }
-        return sampler;
+    _count = count;
+    _type = MaterialParameter::INT_ARRAY;
+}
+
+void MaterialParameter::setVector2(const Vector2& value)
+{
+    setValue(value);
+}
+
+void MaterialParameter::setVector2Array(const Vector2* values, unsigned int count, bool copy)
+{
+    GP_ASSERT(values);
+    clearValue();
+
+    if (copy)
+    {
+        _value.floatPtrValue = new float[2 * count];
+        memcpy(_value.floatPtrValue, const_cast<float*> (&values[0].x), sizeof(float) * 2 * count);
+        _dynamic = true;
     }
-    return NULL;
+    else
+    {
+        _value.floatPtrValue = const_cast<float*> (&values[0].x);
+    }
+
+    _count = count;
+    _type = MaterialParameter::VECTOR2;
+}
+
+void MaterialParameter::setVector3(const Vector3& value)
+{
+    setValue(value);
+}
+
+void MaterialParameter::setVector3Array(const Vector3* values, unsigned int count, bool copy)
+{
+    GP_ASSERT(values);
+    clearValue();
+
+    if (copy)
+    {
+        _value.floatPtrValue = new float[3 * count];
+        memcpy(_value.floatPtrValue, const_cast<float*> (&values[0].x), sizeof(float) * 3 * count);
+        _dynamic = true;
+    }
+    else
+    {
+        _value.floatPtrValue = const_cast<float*> (&values[0].x);
+    }
+
+    _count = count;
+    _type = MaterialParameter::VECTOR3;
+}
+
+void MaterialParameter::setVector4(const Vector4& value)
+{
+    setValue(value);
+}
+
+void MaterialParameter::setVector4Array(const Vector4* values, unsigned int count, bool copy)
+{
+    GP_ASSERT(values);
+    clearValue();
+
+    if (copy)
+    {
+        _value.floatPtrValue = new float[4 * count];
+        memcpy(_value.floatPtrValue, const_cast<float*> (&values[0].x), sizeof(float) * 4 * count);
+        _dynamic = true;
+    }
+    else
+    {
+        _value.floatPtrValue = const_cast<float*> (&values[0].x);
+    }
+
+    _count = count;
+    _type = MaterialParameter::VECTOR4;
+}
+
+void MaterialParameter::setMatrix(const Matrix& value)
+{
+    setValue(value);
+}
+
+void MaterialParameter::setMatrixArray(const Matrix* values, unsigned int count, bool copy)
+{
+    GP_ASSERT(values);
+    clearValue();
+
+    if (copy)
+    {
+        _value.floatPtrValue = new float[16 * count];
+        memcpy(_value.floatPtrValue, const_cast<Matrix&> (values[0]).m, sizeof(float) * 16 * count);
+        _dynamic = true;
+    }
+    else
+    {
+        _value.floatPtrValue = const_cast<Matrix&> (values[0]).m;
+    }
+
+    _count = count;
+    _type = MaterialParameter::MATRIX;
+}
+
+Texture::Sampler* MaterialParameter::setSampler(const char* texturePath, bool generateMipmaps)
+{
+    return setValue(texturePath, generateMipmaps);
+}
+
+void MaterialParameter::setSampler(const Texture::Sampler* value)
+{
+    setValue(value);
+}
+
+void MaterialParameter::setSamplerArray(const Texture::Sampler** values, unsigned int count, bool copy)
+{
+    GP_ASSERT(values);
+    clearValue();
+
+    if (copy)
+    {
+        _value.samplerArrayValue = new const Texture::Sampler*[count];
+        memcpy(_value.samplerArrayValue, values, sizeof(Texture::Sampler*) * count);
+        _dynamic = true;
+    }
+    else
+    {
+        _value.samplerArrayValue = values;
+    }
+
+    for (unsigned int i = 0; i < count; ++i)
+    {
+        const_cast<Texture::Sampler*>(_value.samplerArrayValue[i])->addRef();
+    }
+
+    _count = count;
+    _type = MaterialParameter::SAMPLER_ARRAY;
 }
 
 void MaterialParameter::bind(Effect* effect)
@@ -668,4 +846,9 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
     this->AnimationTarget::cloneInto(materialParameter, context);
 }
 
+MaterialParameter::MethodBinding::MethodBinding(MaterialParameter* param) :
+    _parameter(param), _autoBinding(false)
+{
+}
+
 }

+ 139 - 4
gameplay/src/MaterialParameter.h

@@ -142,6 +142,131 @@ public:
      */
     Texture::Sampler* setValue(const char* texturePath, bool generateMipmaps);
 
+    /**
+     * Stores a float value in this parameter.
+     *
+     * @param value The value to set.
+     */
+    void setFloat(float value);
+
+    /**
+     * Stores an array of float values in this parameter.
+     *
+     * @param values The array of values.
+     * @param count The number of values in the array.
+     * @param copy True to make a copy of the array in the material parameter, or false
+     *      to point to the passed in array/pointer (which must be valid for the lifetime
+     *      of the MaterialParameter).
+     */
+    void setFloatArray(const float* values, unsigned int count, bool copy = false);
+
+    /**
+     * Stores an integer value in this parameter.
+     *
+     * @param value The value to set.
+     */
+    void setInt(int value);
+
+    /**
+     * Stores an array of integer values in this parameter.
+     */
+    void setIntArray(const int* values, unsigned int count, bool copy = false);
+
+    /**
+     * Stores a Vector2 value in this parameter.
+     *
+     * @param value The value to set.
+     */
+    void setVector2(const Vector2& value);
+
+    /**
+     * Stores an array of Vector2 values in this parameter.
+     *
+     * @param values The array of values.
+     * @param count The number of values in the array.
+     * @param copy True to make a copy of the array in the material parameter, or false
+     *      to point to the passed in array/pointer (which must be valid for the lifetime
+     *      of the MaterialParameter).
+     */
+    void setVector2Array(const Vector2* values, unsigned int count, bool copy = false);
+
+    /**
+     * Stores a Vector3 value in this parameter.
+     *
+     * @param value The value to set.
+     */
+    void setVector3(const Vector3& value);
+
+    /**
+     * Stores an array of Vector3 values in this parameter.
+     */
+    void setVector3Array(const Vector3* values, unsigned int count, bool copy = false);
+
+    /**
+     * Stores a Vector4 value in this parameter.
+     *
+     * @param value The value to set.
+     */
+    void setVector4(const Vector4& value);
+
+    /**
+     * Stores an array of Vector4 values in this parameter.
+     *
+     * @param values The array of values.
+     * @param count The number of values in the array.
+     * @param copy True to make a copy of the array in the material parameter, or false
+     *      to point to the passed in array/pointer (which must be valid for the lifetime
+     *      of the MaterialParameter).
+     */
+    void setVector4Array(const Vector4* values, unsigned int count, bool copy = false);
+
+    /**
+     * Stores a Matrix value in this parameter.
+     *
+     * @param value The value to set.
+     */
+    void setMatrix(const Matrix& value);
+
+    /**
+     * Stores an array of Matrix values in this parameter.
+     *
+     * @param values The array of values.
+     * @param count The number of values in the array.
+     * @param copy True to make a copy of the array in the material parameter, or false
+     *      to point to the passed in array/pointer (which must be valid for the lifetime
+     *      of the MaterialParameter).
+     */
+    void setMatrixArray(const Matrix* values, unsigned int count, bool copy = false);
+
+    /**
+     * Loads a texture sampler from the specified path and sets it as the value of this parameter.
+     *
+     * @param texturePath The path to the texture to set.
+     * @param generateMipmaps True to generate a full mipmap chain for the texture, false otherwise.
+     *
+     * @return The texture sampler that was set for this material parameter.
+     */
+    Texture::Sampler* setSampler(const char* texturePath, bool generateMipmaps);
+
+    /**
+     * Stores a Sampler value in this parameter.
+     *
+     * @param value The value to set.
+     */
+    void setSampler(const Texture::Sampler* value);
+
+    /**
+     * Stores an array of Sampler values in this parameter.
+     *
+     * @param values The array of values.
+     * @param count The number of values in the array.
+     * @param copy True to make a copy of the array in the material parameter, or false
+     *      to point to the passed in array/pointer (which must be valid for the lifetime
+     *      of the MaterialParameter).
+     * @script{ignore}
+     */
+    void setSamplerArray(const Texture::Sampler** values, unsigned int count, bool copy = false);
+
     /**
      * Binds the return value of a class method to this material parameter.
      *
@@ -243,10 +368,19 @@ private:
      */
     class MethodBinding : public Ref
     {
+        friend class RenderState;
+
     public:
+
         virtual void setValue(Effect* effect) = 0;
 
     protected:
+
+        /**
+         * Constructor.
+         */
+        MethodBinding(MaterialParameter* param);
+
         /**
          * Destructor.
          */
@@ -256,6 +390,9 @@ private:
          * Hidden copy assignment operator.
          */
         MethodBinding& operator=(const MethodBinding&);
+
+        MaterialParameter* _parameter;
+        bool _autoBinding;
     };
 
     /**
@@ -269,7 +406,6 @@ private:
         MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod);
         void setValue(Effect* effect);
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ValueMethod _valueMethod;
 
@@ -287,7 +423,6 @@ private:
         MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod);
         void setValue(Effect* effect);
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ValueMethod _valueMethod;
         CountMethod _countMethod;
@@ -363,7 +498,7 @@ void MaterialParameter::bindValue(ClassType* classInstance, ParameterType (Class
 
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodValueBinding<ClassType, ParameterType>::MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod) :
-    _parameter(param), _instance(instance), _valueMethod(valueMethod)
+    MethodBinding(param), _instance(instance), _valueMethod(valueMethod)
 {
 }
 
@@ -375,7 +510,7 @@ void MaterialParameter::MethodValueBinding<ClassType, ParameterType>::setValue(E
 
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodArrayBinding<ClassType, ParameterType>::MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod) :
-    _parameter(param), _instance(instance), _valueMethod(valueMethod), _countMethod(countMethod)
+    MethodBinding(param), _instance(instance), _valueMethod(valueMethod), _countMethod(countMethod)
 {
 }
 

+ 1 - 1
gameplay/src/Matrix.h

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

+ 66 - 0
gameplay/src/MeshBatch.cpp

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

+ 26 - 2
gameplay/src/MeshBatch.h

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

+ 2 - 57
gameplay/src/MeshBatch.inl

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

+ 2 - 2
gameplay/src/MeshSkin.cpp

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

+ 1 - 0
gameplay/src/MeshSkin.h

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

+ 10 - 7
gameplay/src/Model.cpp

@@ -54,17 +54,20 @@ unsigned int Model::getMeshPartCount() const
 
 Material* Model::getMaterial(int partIndex)
 {
-    GP_ASSERT(partIndex == -1 || (partIndex >= 0 && partIndex < (int)getMeshPartCount()));
+    GP_ASSERT(partIndex == -1 || partIndex >= 0);
 
     Material* m = NULL;
 
-    if (partIndex >= 0 && partIndex < (int)_partCount)
+    if (partIndex < 0)
+        return _material;
+
+    if (partIndex >= (int)_partCount)
+        return NULL;
+
+    // Look up explicitly specified part material.
+    if (_partMaterials)
     {
-        // Look up explicitly specified part material.
-        if (_partMaterials)
-        {
-            m = _partMaterials[partIndex];
-        }
+        m = _partMaterials[partIndex];
     }
 
     if (m == NULL)

+ 1 - 0
gameplay/src/Model.h

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

+ 65 - 23
gameplay/src/Node.cpp

@@ -126,6 +126,8 @@ void Node::addChild(Node* child)
 
     ++_childCount;
 
+    setBoundsDirty();
+
     if (_notifyHierarchyChanged)
     {
         hierarchyChanged();
@@ -376,13 +378,15 @@ unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool rec
 
 Scene* Node::getScene() const
 {
-    // Search for a scene in our parents.
-    for (Node* n = const_cast<Node*>(this); n != NULL; n = n->getParent())
+    if (_scene)
+        return _scene;
+
+    // Search our parent for the scene
+    if (_parent)
     {
-        if (n->_scene)
-        {
-            return n->_scene;
-        }
+        Scene* scene = _parent->getScene();
+        if (scene)
+            return scene;
     }
 
     return NULL;
@@ -398,6 +402,11 @@ Node* Node::getRootNode() const
     return n;
 }
 
+bool Node::isStatic() const
+{
+    return (_collisionObject && _collisionObject->isStatic());
+}
+
 const Matrix& Node::getWorldMatrix() const
 {
     if (_dirtyBits & NODE_DIRTY_WORLD)
@@ -406,23 +415,26 @@ const Matrix& Node::getWorldMatrix() const
         // parent calls our getWorldMatrix() method as a result of the following calculations.
         _dirtyBits &= ~NODE_DIRTY_WORLD;
 
-        // If we have a parent, multiply our parent world transform by our local
-        // transform to obtain our final resolved world transform.
-        Node* parent = getParent();
-        if (parent && (!_collisionObject || _collisionObject->isKinematic()))
-        {
-            Matrix::multiply(parent->getWorldMatrix(), getMatrix(), &_world);
-        }
-        else
+        if (!isStatic())
         {
-            _world = getMatrix();
-        }
+            // If we have a parent, multiply our parent world transform by our local
+            // transform to obtain our final resolved world transform.
+            Node* parent = getParent();
+            if (parent && (!_collisionObject || _collisionObject->isKinematic()))
+            {
+                Matrix::multiply(parent->getWorldMatrix(), getMatrix(), &_world);
+            }
+            else
+            {
+                _world = getMatrix();
+            }
 
-        // Our world matrix was just updated, so call getWorldMatrix() on all child nodes
-        // to force their resolved world matrices to be updated.
-        for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
-        {
-            child->getWorldMatrix();
+            // Our world matrix was just updated, so call getWorldMatrix() on all child nodes
+            // to force their resolved world matrices to be updated.
+            for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
+            {
+                child->getWorldMatrix();
+            }
         }
     }
 
@@ -766,6 +778,8 @@ void Node::setLight(Light* light)
             _light->addRef();
             _light->setNode(this);
         }
+
+        setBoundsDirty();
     }
 }
 
@@ -816,6 +830,8 @@ void Node::setTerrain(Terrain* terrain)
             _terrain->addRef();
             _terrain->setNode(this);
         }
+
+        setBoundsDirty();
     }
 }
 
@@ -872,7 +888,27 @@ const BoundingSphere& Node::getBoundingSphere() const
                 _bounds.merge(_model->getMesh()->getBoundingSphere());
             }
         }
-        else
+        if (_light)
+        {
+            switch (_light->getLightType())
+            {
+            case Light::POINT:
+                if (empty)
+                {
+                    _bounds.set(Vector3::zero(), _light->getRange());
+                    empty = false;
+                }
+                else
+                {
+                    _bounds.merge(BoundingSphere(Vector3::zero(), _light->getRange()));
+                }
+                break;
+            case Light::SPOT:
+                // TODO: Implement spot light bounds
+                break;
+            }
+        }
+        if (empty)
         {
             // Empty bounding sphere, set the world translation with zero radius
             worldMatrix.getTranslation(&_bounds.center);
@@ -933,7 +969,6 @@ const BoundingSphere& Node::getBoundingSphere() const
     return _bounds;
 }
 
-
 Node* Node::clone() const
 {
     NodeCloneContext context;
@@ -953,11 +988,13 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
     Node* copy = cloneSingleNode(context);
     GP_ASSERT(copy);
 
+    // Find our current last child
     Node* lastChild = NULL;
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     {
         lastChild = child;
     }
+
     // Loop through the nodes backwards because addChild adds the node to the front.
     for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     {
@@ -966,6 +1003,7 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
         copy->addChild(childCopy);
         childCopy->release();
     }
+
     return copy;
 }
 
@@ -1002,6 +1040,10 @@ void Node::cloneInto(Node* node, NodeCloneContext &context) const
     }
     node->_world = _world;
     node->_bounds = _bounds;
+
+    // Note: Do not clone _userData - we can't make any assumptions about its content and how it's managed,
+    // so it's the caller's responsibility to clone user data if needed.
+
     if (_tags)
     {
         node->_tags = new std::map<std::string, std::string>(_tags->begin(), _tags->end());

+ 13 - 1
gameplay/src/Node.h

@@ -29,6 +29,7 @@ class Node : public Transform, public Ref
     friend class Scene;
     friend class Bundle;
     friend class MeshSkin;
+    friend class Light;
 
 public:
 
@@ -223,12 +224,23 @@ public:
      *
      * @return The scene.
      */
-    Scene* getScene() const;
+    virtual Scene* getScene() const;
 
     /**
      * Gets the top level node in this node's parent hierarchy.
      */
     Node* getRootNode() const;
+    
+    /**
+     * Returns whether the transformation of this node is static.
+     *
+     * Nodes that have static rigid bodies attached to them are considered static.
+     *
+     * @return True if the transformation of this Node is static, false otherwise.
+     *
+     * @see Transform::isStatic()
+     */
+    bool isStatic() const;
 
     /**
      * Gets the world matrix corresponding to this node.

+ 3 - 3
gameplay/src/ParticleEmitter.cpp

@@ -111,8 +111,8 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 
     // Load sprite properties.
     // Path to image file is required.
-    const char* texturePath = sprite->getString("path");
-    if (!texturePath || strlen(texturePath) == 0)
+    std::string texturePath;
+    if (!sprite->getPath("path", &texturePath))
     {
         GP_ERROR("Failed to load particle emitter: required image file path ('path') is missing.");
         return NULL;
@@ -184,7 +184,7 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
     bool orbitAcceleration = properties->getBool("orbitAcceleration");
 
     // Apply all properties to a newly created ParticleEmitter.
-    ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
+    ParticleEmitter* emitter = ParticleEmitter::create(texturePath.c_str(), textureBlending, particleCountMax);
     if (!emitter)
     {
         GP_ERROR("Failed to create particle emitter.");

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

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.