Kaynağa Gözat

Merge branch 'next' of https://github.com/blackberry/GamePlay

Conflicts:
	CMakeLists.txt
	gameplay/src/Bundle.cpp
	gameplay/src/PlatformLinux.cpp
	gameplay/src/PlatformWindows.cpp
	gameplay/src/VerticalLayout.cpp
	install.bat
	install.sh
seanpaultaylor 12 yıl önce
ebeveyn
işleme
1af407bad3
100 değiştirilmiş dosya ile 14686 ekleme ve 14099 silme
  1. 46 0
      .gitattributes
  2. 28 38
      .gitignore
  3. 257 210
      CHANGES.md
  4. 1 1
      CMakeLists.txt
  5. 41 42
      README.md
  6. 3 3
      api/README.md
  7. 1 1
      gameplay.doxyfile
  8. 153 336
      gameplay.sln
  9. 0 3
      gameplay.xcworkspace/contents.xcworkspacedata
  10. 6 0
      gameplay/.cproject
  11. 44 24
      gameplay/CMakeLists.txt
  12. 2 2
      gameplay/android/AndroidManifest.xml
  13. 33 0
      gameplay/android/java/.project
  14. 9 0
      gameplay/android/java/AndroidManifest.xml
  15. 19 31
      gameplay/android/java/build.xml
  16. 188 0
      gameplay/android/java/src/org/gameplay3d/GamePlayActivity.java
  17. 1135 0
      gameplay/android/java/src/org/gameplay3d/GooglePlayClientHelper.java
  18. 117 0
      gameplay/android/java/src/org/gameplay3d/GooglePlaySocial.java
  19. 11 4
      gameplay/android/jni/Android.mk
  20. 2 1
      gameplay/android/jni/Application.mk
  21. 43 260
      gameplay/gameplay.vcxproj
  22. 616 568
      gameplay/gameplay.vcxproj.filters
  23. 1190 1741
      gameplay/gameplay.xcodeproj/project.pbxproj
  24. 0 7
      gameplay/gameplay.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  25. BIN
      gameplay/gameplay.xcodeproj/project.xcworkspace/xcuserdata/mozeal.xcuserdatad/UserInterfaceState.xcuserstate
  26. 1 1
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-ios.xcscheme
  27. 1 1
      gameplay/gameplay.xcodeproj/xcshareddata/xcschemes/gameplay-macosx.xcscheme
  28. 0 0
      gameplay/res/design/arial.ttf
  29. 0 0
      gameplay/res/design/icon_128.ico
  30. 0 0
      gameplay/res/design/icon_16.ico
  31. 0 0
      gameplay/res/design/icon_32.ico
  32. 0 0
      gameplay/res/design/icon_64.ico
  33. 0 0
      gameplay/res/design/logo.ai
  34. 41 0
      gameplay/res/materials/terrain.material
  35. 0 45
      gameplay/res/shaders/colored-unlit.frag
  36. 0 53
      gameplay/res/shaders/colored-unlit.vert
  37. 134 74
      gameplay/res/shaders/colored.frag
  38. 137 82
      gameplay/res/shaders/colored.vert
  39. 24 1
      gameplay/res/shaders/font.frag
  40. 3 0
      gameplay/res/shaders/font.vert
  41. 0 15
      gameplay/res/shaders/form.frag
  42. 0 16
      gameplay/res/shaders/form.vert
  43. 0 41
      gameplay/res/shaders/lighting-directional.frag
  44. 0 31
      gameplay/res/shaders/lighting-directional.vert
  45. 0 46
      gameplay/res/shaders/lighting-point.frag
  46. 0 50
      gameplay/res/shaders/lighting-point.vert
  47. 0 63
      gameplay/res/shaders/lighting-spot.frag
  48. 0 52
      gameplay/res/shaders/lighting-spot.vert
  49. 93 46
      gameplay/res/shaders/lighting.frag
  50. 67 0
      gameplay/res/shaders/lighting.vert
  51. 24 26
      gameplay/res/shaders/skinning-none.vert
  52. 84 93
      gameplay/res/shaders/skinning.vert
  53. 21 15
      gameplay/res/shaders/sprite.frag
  54. 3 0
      gameplay/res/shaders/sprite.vert
  55. 94 37
      gameplay/res/shaders/terrain.frag
  56. 80 19
      gameplay/res/shaders/terrain.vert
  57. 0 76
      gameplay/res/shaders/textured-bumped.frag
  58. 0 106
      gameplay/res/shaders/textured-bumped.vert
  59. 0 47
      gameplay/res/shaders/textured-unlit.frag
  60. 0 57
      gameplay/res/shaders/textured-unlit.vert
  61. 147 73
      gameplay/res/shaders/textured.frag
  62. 167 92
      gameplay/res/shaders/textured.vert
  63. BIN
      gameplay/res/ui/arial.gpb
  64. 1 1
      gameplay/src/AIController.h
  65. 1 1
      gameplay/src/AIMessage.h
  66. 1 3
      gameplay/src/AIState.h
  67. 143 144
      gameplay/src/AIStateMachine.cpp
  68. 57 54
      gameplay/src/AbsoluteLayout.cpp
  69. 67 67
      gameplay/src/AbsoluteLayout.h
  70. 476 477
      gameplay/src/Animation.cpp
  71. 241 239
      gameplay/src/Animation.h
  72. 666 666
      gameplay/src/AnimationClip.cpp
  73. 430 430
      gameplay/src/AnimationClip.h
  74. 136 136
      gameplay/src/AnimationController.cpp
  75. 106 106
      gameplay/src/AnimationController.h
  76. 560 560
      gameplay/src/AnimationTarget.cpp
  77. 248 248
      gameplay/src/AnimationTarget.h
  78. 73 73
      gameplay/src/AnimationValue.cpp
  79. 88 88
      gameplay/src/AnimationValue.h
  80. 403 403
      gameplay/src/AudioBuffer.cpp
  81. 57 57
      gameplay/src/AudioBuffer.h
  82. 106 106
      gameplay/src/AudioController.cpp
  83. 66 66
      gameplay/src/AudioController.h
  84. 147 140
      gameplay/src/AudioListener.cpp
  85. 176 172
      gameplay/src/AudioListener.h
  86. 306 306
      gameplay/src/AudioSource.cpp
  87. 211 207
      gameplay/src/AudioSource.h
  88. 358 352
      gameplay/src/Base.h
  89. 349 349
      gameplay/src/BoundingBox.cpp
  90. 233 233
      gameplay/src/BoundingBox.h
  91. 19 19
      gameplay/src/BoundingBox.inl
  92. 324 324
      gameplay/src/BoundingSphere.cpp
  93. 184 184
      gameplay/src/BoundingSphere.h
  94. 19 19
      gameplay/src/BoundingSphere.inl
  95. 1860 1805
      gameplay/src/Bundle.cpp
  96. 465 448
      gameplay/src/Bundle.h
  97. 63 158
      gameplay/src/Button.cpp
  98. 96 135
      gameplay/src/Button.h
  99. 499 445
      gameplay/src/Camera.cpp
  100. 385 348
      gameplay/src/Camera.h

+ 46 - 0
.gitattributes

@@ -0,0 +1,46 @@
+# Set default behaviour, in case users don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files we want to always be normalized and converted to native line endings on checkout.
+*.cpp text
+*.c text
+*.mm text
+*.m text
+*.h text
+*.inl text
+*.lua text
+*.vert text
+*.frag text
+*.material text
+*.form text
+*.theme text
+*.particle text
+*.animation text
+*.audio text
+*.terrain text
+*.config text
+*.txt text
+*.xml text
+*.md text
+*.sh text
+
+# Declare windows files that will always have CRLF line endings on checkout.
+*.sln text eol=crlf
+*.vcxproj text eol=crlf
+*.vcxproj.filters text eol=crlf
+*.bat text eol=crlf
+
+# Denote all files that are truly binary and should not be modified.
+*.png binary
+*.jpg binary
+*.psd binary
+*.dds binary
+*.pvr binary
+*.r16 binary
+*.fbx binary
+*.mb binary
+*.ogg binary
+*.wav binary
+*.raw binary
+*.ttf binary
+*.gpb binary

+ 28 - 38
.gitignore

@@ -31,6 +31,7 @@ Thumbs.db
 /gameplay-docs
 /api/html
 /gameplay-tests
+gameplay.xcworkspace/xcshareddata/gameplay.xccheckout
 
 /gameplay/Debug
 /gameplay/DebugMem
@@ -56,12 +57,14 @@ Thumbs.db
 /tools/gameplay-encoder/Release
 /tools/gameplay-encoder/gameplay-encoder.xcodeproj/xcuserdata
 
-/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
+/tools/luagen/Release
+/tools/luagen/Debug
+/tools/luagen/gameplay-luagen.xcodeproj/xcuserdata
+/tools/luagen/doxygen_entrydb_680.tmp
+/tools/luagen/doxygen_objdb_680.tmp
+/tools/luagen/xml
+/tools/luagen/gameplay-luagen.xcodeproj/project.xcworkspace/
+
 
 /samples/browser/Debug
 /samples/browser/DebugMem
@@ -73,7 +76,8 @@ Thumbs.db
 /samples/browser/Device-Coverage
 /samples/browser/Device-Profile
 /samples/browser/Device-Release
-/samples/browser/res/shaders
+/samples/browser/res/shaders
+/samples/browser/res/ui
 /samples/browser/res/logo_powered_white.png
 /samples/browser/android/src
 /samples/browser/android/assets
@@ -100,7 +104,8 @@ Thumbs.db
 /samples/character/Device-Release
 /samples/character/Device-Release-QC
 /samples/character/game.config
-/samples/character/res/shaders
+/samples/character/res/shaders
+/samples/character/res/ui
 /samples/character/res/logo_powered_white.png
 /samples/character/android/project.properties
 /samples/character/android/proguard.cfg
@@ -115,31 +120,6 @@ Thumbs.db
 /samples/character/res/gamepad.xcf
 /samples/character/sample-character.xcodeproj/xcuserdata
 
-/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
-
 /samples/lua/Debug
 /samples/lua/DebugMem
 /samples/lua/Release
@@ -150,7 +130,8 @@ Thumbs.db
 /samples/lua/Device-Coverage
 /samples/lua/Device-Profile
 /samples/lua/Device-Release
-/samples/lua/res/shaders
+/samples/lua/res/shaders
+/samples/lua/res/ui
 /samples/lua/res/logo_powered_white.png
 /samples/lua/android/src
 /samples/lua/android/assets
@@ -174,7 +155,8 @@ Thumbs.db
 /samples/mesh/Device-Coverage
 /samples/mesh/Device-Profile
 /samples/mesh/Device-Release
-/samples/mesh/res/shaders
+/samples/mesh/res/shaders
+/samples/mesh/res/ui
 /samples/mesh/res/logo_powered_white.png
 /samples/mesh/android/src
 /samples/mesh/android/assets
@@ -198,7 +180,8 @@ Thumbs.db
 /samples/particles/Device-Coverage
 /samples/particles/Device-Profile
 /samples/particles/Device-Release
-/samples/particles/res/shaders
+/samples/particles/res/shaders
+/samples/particles/res/ui
 /samples/particles/res/logo_powered_white.png
 /samples/particles/Device-Debug
 /samples/particles/Debug
@@ -227,7 +210,8 @@ Thumbs.db
 /samples/racer/Device-Release
 /samples/racer/Device-Release-QC
 /samples/racer/game.config
-/samples/racer/res/shaders
+/samples/racer/res/shaders
+/samples/racer/res/ui
 /samples/racer/res/logo_powered_white.png
 /samples/racer/android/project.properties
 /samples/racer/android/proguard.cfg
@@ -254,7 +238,8 @@ Thumbs.db
 /samples/spaceship/Device-Coverage
 /samples/spaceship/Device-Profile
 /samples/spaceship/Device-Release
-/samples/spaceship/res/shaders
+/samples/spaceship/res/shaders
+/samples/spaceship/res/ui
 /samples/spaceship/res/logo_powered_white.png
 /samples/spaceship/android/NUL
 /samples/spaceship/android/project.properties
@@ -268,3 +253,8 @@ Thumbs.db
 /samples/spaceship/android/local.properties
 /samples/spaceship/sample-spaceship.xcodeproj/xcuserdata
 
+/tools/encoder/Debug
+/tools/encoder/Release
+
+/tools/luagen/Debug
+/tools/luagen/Release

+ 257 - 210
CHANGES.md

@@ -1,210 +1,257 @@
-## 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. 
-- Adds Terrain class to support for heightmap based terrains featuring LOD, multiple surface layers, loading from PNG, RAW8/16, full transform, physics, patch culling and verticle skirt for cracks.
-- Adds object-space normal map generation to gameplay-encoder for terrain normal map generation.
-- Adds scene support for loading .terrain files in .scene files.
-- Adds scene support for inline cameras to .scene files.
-- Adds suppoft for defining .scene files without 'path' to gpb. New node can not be create in .scene file.
-- Adds static Scene::getScene(const char*) to query currently active scenes in a game, helpful for script access.
-- Adds support for multiple translate, rotate and scale commands in a single node entity within .scene files, processed in-order they are defined.
-- Adds scene support for material auto binding scene ambient color, light color and light direction.
-- Adds support for setting the depth compare function on materials.
-- Adds support for texture/sampler arrays being passed to materials.
-- Adds support for loading uncompressed DDS textures for the following formats: R8G8B8, A8R8G8B8, A8B8G8R8, X8R8G8B8, X8B8G8R8
-- Adds improvments to prefer higher quality mipmap generation.
-- Adds improved Gamepad API support for button enumeration, triggers and some mobile Gamepad support on BlackBerry.
-- Adds additional gameplay-tests for billboards, forms, gamepads and lights.
-- Adds support for launching the browser via launchURL(const char*).
-- Adds physics support for setLinearFactor and setAngularFactor  on rigid bodies.
-- Adds methods to PhysicsCollisionObject to allow conversion to subclass types (i.e. PhysicsRigidBody, PhysicsCharacter, etc) from script.
-- Adds option for fullscreen without width/height config to use native desktop resolution.
-- Adds Linux support for OpenAL PulseAudio back-end.
-- Adds support for latest Bullet Physics 2.81 with NEON optimizations for mobile targets.
-- Adds support for preprocessor directive NO_LUA_BINDINGS in the gameplay project to omit inclusion of generated lua bindings in compilation for developer mode value. 
-- Adds optimizations to Lua generator to only write generated files if they differ from existing files, reducing both build times and committing of unchanged script binding files.
-- Adds changes to Slider for setValueTextVisible, setValueTextAlignment, setValueTextPrecision and getters.
-- Adds Microsoft Windows 7 64-bit support.
-- Adds Apple iOS 6 support.
-- Fixes to external-deps to reduce the size of the libraries on Windows.
-- Fixes for Android to no longer need to copy files to the SD card before reading them. None of the Android samples require an SD card.
-- Fixes for animation of opacity on UI and fonts.
-- Fixes in UI for removing controls and also setVisible(bool).
-- Fixes for UI controls missing on MacOSX.
-- Fixes for setting UI alignment programmatically.
-- Fixes for lighting shaders.
-- Fixes to the texture minification mode from GL_LINEAR_MIPMAP_LINEAR to GL_NEAREST_MIPMAP_LINEAR for newly created textures with mipmaps.
-- Fixes minor memory leaks and possible access violations when calling Game::exit() from script.
-- Fixes physics debug drawing for large scenes causing the internal MeshBatch to grow to an enormous size.
-
-## v1.5.0
-
-- Linux support. (tested on Ubuntu 12)
-- CMake support for makefile generation for Linux.
-- Gamepad API support for desktops.
-- Touch gesture support for tap, swipe and pinch.
-- Vehicle physics support via new PhysicsVehicle and PhysicsVehicleWheel classes.
-- Adds new racer sample (sample06-racer).
-- Adds gameplay-tests project as a test app for various basic engine features with some initial tests.
-- Adds support for Scene files for wildcard identifiers.
-- Adds Visual Studio Plug-in support for BlackBerry PlayBook and BlackBerry 10.
-- Adds configurable multi-sampling anti-aliasing support. 
-- Adds updates to latest FBX SDK 2013.3.
-- Adds file formats documentation for game.config .scene, .material, .animation, .physics, .particle
-- Adds Game/Platform::canExit for testing device capabilities to quit. (only ios)
-- Web community forums at http://www.gameplay3d.org/forums.
-- Changed keyTimes from unsigned long[]  to unsigned int[]. (breaks compat. in AnimationTarget and Animation::Channel)
-- Fixes inconsistencies from Bundle::getObjectID() to Bundle::getObjectId() (breaks compat. in Bundle)
-- Fixes the texture coordinates of Mesh::createQuad(float x, float y, float width, float height).
-- Fixes line-wise distortion when loading RGB png's into textures that are non-power of two.
-- Fixes inconsistencies in createXXXX methods.  (breaks compat. in Scene)
-- Fixes Rectangle::contains.
-- Fixes Lua print logging.
-- Fixes Lua errors to be treated as runtime warnings.
-- Fixes setVertexData to pointers instead of constant data.
-- Fixes AudioSource so that it doesn't loop by default.
-- Fixes minor UI scrolling issues.
-
-## v1.4.0
-
-- Lua script bindings for all gameplay interfaces.
-- Lua script binding generator tool (gameplay-luagen) for generating gameplay Lua bindings from doxygen xml output.
-- AIController, AIAgent, AIStateMachine, AIState and AIMessage classes for scripted AI support.
-- Sample for sample05-lua to demonstrate basic Lua with AI scripting.
-- Gamepad class with virtual gamepad support.
-- Pre-built versions gameplay-encoder added to bin folder with TTF, DAE and FBX support built-in.
-- Improved modular shaders with support for #include in shaders. (breaks compat. for shaders)
-- LightMap support into colored-unlit.frag and textured-unlit.frag shaders.
-- Adds cloning and wireframing features to sample03-character.
-- Adds kick the ball on the sample03-character to demonstrate 2 buttons and more physics.
-- Fixes missing mouse events on UI controls.
-- Fixes to gameplay-encoder to prompt user for font size if not specified.
-- Fixes to add "-g" as short form argument for grouping animations.
-- Fixes node cloning.
-- Fixes to gameplay-encoder for output file path when encoding fonts.
-- Fixes to FrameBuffer, RenderTarget and DepthStencilTarget.
-- Fixes user switching in MacOSX to other applications with Apple-Tab.
-- Fixes measureText with empty string to be proper size.
-- Fixes for aliased text by applying linear filtering by default on Fonts.
-- Fixes RenderState::StateBlock::bindNoRestore() issue where blend function was not restored to the proper defaults.
-- Fixes some inconsistencies in Game event method names for menuEvent. (breaks compat. in Game)
-- Fixes some inconsistencies with AnimationClip::getID() to be same as Node::getId() and other classes. (breaks compat. in AnimationClip)
-- Fixes to gameplay-encoder and PhysicsRigidBody for heightmaps which adds height precision into generated heightmap png's.
-
-## v1.3.0
-
-- Portrait mode games on mobile platforms.
-- Fullscreen and configurable game resolutions on desktop platforms.
-- User Interface support for scrolling with scrollbars on Container.
-- PVRTC, ATC and DXT texture compression support.
-- Performance improvements in user interface forms and text.
-- Performance improvements in animations on transforms.
-- Performance improvements using NEON math for BlackBerry and iOS.
-- Fixes for improvements in error handling throughout all systems.
-- Fixes supporting built-in Maya COLLADA exporter via DAE_FBX export.
-- Fixes for latest FBX SDK 2013 support.
-- Fixes for loading from some WAV files that were crashing.
-- Fixes for From/By animations.
-- Fixes allowing all inline properties loaded within .scene files. (breaks compat. for .scene)
-- Fixes in .scene files for collisionObject definitions (breaks compat. for .scene)
-- Fixes for depth/z-ordering of controls.
-
-## v1.2.0
-
-- BlackBerry 10 support.
-- iOS 5.1 support.
-- Android 2.3+ support.
-- User interface system with declarative forms and themes.
-- Bluetooth keyboard/mouse support on BlackBerry platform.
-- Developer guide.
-- Sample/tutorial for sample03-character.
-- Sample for sample04-particles to demonstrate particle emitters.
-- Fixes for loading properties from URL.
-- Fixes on Win32/MacOSX for when mouse pointer leaves the window and returns.
-- Fixes to accelerometer for Android.
-- Fixes in animation blending.
-- Fixes to GPB for loading from single node and parent node. (breaks compat. for .gpb)
-
-## v1.1.0
-
-- FBX support in gameplay-encoder.
-- MacOSX platform support using XCode.
-- Off-screen rendering functionality using FrameBuffer.
-- Loading 3D scenes using declarative  .scene files.
-- Loading audio from .ogg files using vorbis.
-- Loading AudioSources from .audio files.
-- Loading Animations from .animation files.
-- AnimationClip support for cross fading.
-- Physics support using Bullet Physics.
-- Cross-platform new project generator.
-- Overloaded operators in Math classes.
-- Font improvements for justify, clip, wrap and scaling.
-- Fixes for Font::drawText to use point size and not float scalar.
-- Fixes for memory leaks in and fixes to AnimationTarget.
-- Fixes for bumped and parallax shaders.
-- Fixes to simplify folders for resources in samples.
-- Fixes to the material/shader system.
-- Fixes to the ParticleEmitter.
-
-## v1.0.0
-
-- Initial release.
-
-
-
-
+## v2.0.0
+
+- Adds support for Visual Studio 2013.
+- Adds support for iOS 7, MacOS X 10.8 and XCode 5.
+- Adds support for BlackBerry NDK 10.2.
+- Adds support for CollisionObject group mask filtering.
+- Adds support in shaders for mulitple lights using shader defines.
+- Adds various improvements to Material binding support.
+- Adds support for array MaterialParameter's.
+- Adds support for encoding distance field based fonts.
+- Adds support for multiple sizes of fonts encoded into single bundle.
+- Adds improvements for simplified layout for forms and theming.
+- Adds ControlFactory to instantiate core controls and support users registering custom controls.
+- Adds support for percentage based layout definitions for controls in forms.
+- Adds password mode on Textbox controls.
+- Adds improvements to Textbox key repeat and cursor jump modifiers.
+- Adds FileSystem::displayFileDialog on all desktop platforms.
+- Adds Terrain support for material definition for user customization.
+- Adds support to RenderState FrontFace.
+- Adds Node::setActive, getActive and getActiveInHierachy.
+- Adds ETC compressed texture support.
+- Adds gameplay::strcmpnocase as a platform-independent case-insensitive string compare function.
+- Adds new gestures for Long Tap, Drag and Drop.
+- Adds support for Android pinch gestures.
+- Adds version number to encoder output files.
+- Fixes to specular and bumped shader for spot lights.
+- Fixes to UI rendering quality from FrameBuffer usage which is no longer used.
+- Fixes to Texbox focus related issues.
+- Fixes to Label clipping and Labels to automatically re-size around their text.
+- Fixes in Camera related to invalid project() and setActiveCamera() issues.
+- Fixes for Linux VSYNC and Mouse capture.
+- Fixes various logging and warning problems and annoyances.
+- Fixes bitmap font rendering quality in gameplay-encoder and Font class.
+- Fixes Mesh CollisionShape's.
+- Fixes for destroying CollisionObject that does not notify CollisionListener.
+- Fixes to PhysicsVehicle where wheels detach and may collide with body.
+- Fixes to encoder spotlight for properly extracting inner and outer angle from FBX.
+- Fixes to invalid assertion in AnimationClip.
+- Fixes for FrameBuffer width/height goeing out of date.
+- Fixes for incorrect RenderState Type specifier.
+- Fixes to FileSystem for Windows file paths.
+- Fixes to improve speed of installation of binaries via install.sh/.bat scripts.
+- Fixes related to disable and non-visible controls not receiving input.
+- Fixes to Bundle, Font, Properties and SceneLoader to use GP_WARN and improve locations of errors.
+- Fixes to encoder related to Material generation.
+
+
+## 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. 
+- Adds Terrain class to support for heightmap based terrains featuring LOD, multiple surface layers, loading from PNG, RAW8/16, full transform, physics, patch culling and verticle skirt for cracks.
+- Adds object-space normal map generation to gameplay-encoder for terrain normal map generation.
+- Adds scene support for loading .terrain files in .scene files.
+- Adds scene support for inline cameras to .scene files.
+- Adds suppoft for defining .scene files without 'path' to gpb. New node can not be create in .scene file.
+- Adds static Scene::getScene(const char*) to query currently active scenes in a game, helpful for script access.
+- Adds support for multiple translate, rotate and scale commands in a single node entity within .scene files, processed in-order they are defined.
+- Adds scene support for material auto binding scene ambient color, light color and light direction.
+- Adds support for setting the depth compare function on materials.
+- Adds support for texture/sampler arrays being passed to materials.
+- Adds support for loading uncompressed DDS textures for the following formats: R8G8B8, A8R8G8B8, A8B8G8R8, X8R8G8B8, X8B8G8R8
+- Adds improvments to prefer higher quality mipmap generation.
+- Adds improved Gamepad API support for button enumeration, triggers and some mobile Gamepad support on BlackBerry.
+- Adds additional gameplay-tests for billboards, forms, gamepads and lights.
+- Adds support for launching the browser via launchURL(const char*).
+- Adds physics support for setLinearFactor and setAngularFactor  on rigid bodies.
+- Adds methods to PhysicsCollisionObject to allow conversion to subclass types (i.e. PhysicsRigidBody, PhysicsCharacter, etc) from script.
+- Adds option for fullscreen without width/height config to use native desktop resolution.
+- Adds Linux support for OpenAL PulseAudio back-end.
+- Adds support for latest Bullet Physics 2.81 with NEON optimizations for mobile targets.
+- Adds support for preprocessor directive NO_LUA_BINDINGS in the gameplay project to omit inclusion of generated lua bindings in compilation for developer mode value. 
+- Adds optimizations to Lua generator to only write generated files if they differ from existing files, reducing both build times and committing of unchanged script binding files.
+- Adds changes to Slider for setValueTextVisible, setValueTextAlignment, setValueTextPrecision and getters.
+- Adds Microsoft Windows 7 64-bit support.
+- Adds Apple iOS 6 support.
+- Fixes to external-deps to reduce the size of the libraries on Windows.
+- Fixes for Android to no longer need to copy files to the SD card before reading them. None of the Android samples require an SD card.
+- Fixes for animation of opacity on UI and fonts.
+- Fixes in UI for removing controls and also setVisible(bool).
+- Fixes for UI controls missing on MacOSX.
+- Fixes for setting UI alignment programmatically.
+- Fixes for lighting shaders.
+- Fixes to the texture minification mode from GL_LINEAR_MIPMAP_LINEAR to GL_NEAREST_MIPMAP_LINEAR for newly created textures with mipmaps.
+- Fixes minor memory leaks and possible access violations when calling Game::exit() from script.
+- Fixes physics debug drawing for large scenes causing the internal MeshBatch to grow to an enormous size.
+
+## v1.5.0
+
+- Linux support. (tested on Ubuntu 12)
+- CMake support for makefile generation for Linux.
+- Gamepad API support for desktops.
+- Touch gesture support for tap, swipe and pinch.
+- Vehicle physics support via new PhysicsVehicle and PhysicsVehicleWheel classes.
+- Adds new racer sample (sample06-racer).
+- Adds gameplay-tests project as a test app for various basic engine features with some initial tests.
+- Adds support for Scene files for wildcard identifiers.
+- Adds Visual Studio Plug-in support for BlackBerry PlayBook and BlackBerry 10.
+- Adds configurable multi-sampling anti-aliasing support. 
+- Adds updates to latest FBX SDK 2013.3.
+- Adds file formats documentation for game.config .scene, .material, .animation, .physics, .particle
+- Adds Game/Platform::canExit for testing device capabilities to quit. (only ios)
+- Web community forums at http://www.gameplay3d.org/forums.
+- Changed keyTimes from unsigned long[]  to unsigned int[]. (breaks compat. in AnimationTarget and Animation::Channel)
+- Fixes inconsistencies from Bundle::getObjectID() to Bundle::getObjectId() (breaks compat. in Bundle)
+- Fixes the texture coordinates of Mesh::createQuad(float x, float y, float width, float height).
+- Fixes line-wise distortion when loading RGB png's into textures that are non-power of two.
+- Fixes inconsistencies in createXXXX methods.  (breaks compat. in Scene)
+- Fixes Rectangle::contains.
+- Fixes Lua print logging.
+- Fixes Lua errors to be treated as runtime warnings.
+- Fixes setVertexData to pointers instead of constant data.
+- Fixes AudioSource so that it doesn't loop by default.
+- Fixes minor UI scrolling issues.
+
+## v1.4.0
+
+- Lua script bindings for all gameplay interfaces.
+- Lua script binding generator tool (gameplay-luagen) for generating gameplay Lua bindings from doxygen xml output.
+- AIController, AIAgent, AIStateMachine, AIState and AIMessage classes for scripted AI support.
+- Sample for sample05-lua to demonstrate basic Lua with AI scripting.
+- Gamepad class with virtual gamepad support.
+- Pre-built versions gameplay-encoder added to bin folder with TTF, DAE and FBX support built-in.
+- Improved modular shaders with support for #include in shaders. (breaks compat. for shaders)
+- LightMap support into colored-unlit.frag and textured-unlit.frag shaders.
+- Adds cloning and wireframing features to sample03-character.
+- Adds kick the ball on the sample03-character to demonstrate 2 buttons and more physics.
+- Fixes missing mouse events on UI controls.
+- Fixes to gameplay-encoder to prompt user for font size if not specified.
+- Fixes to add "-g" as short form argument for grouping animations.
+- Fixes node cloning.
+- Fixes to gameplay-encoder for output file path when encoding fonts.
+- Fixes to FrameBuffer, RenderTarget and DepthStencilTarget.
+- Fixes user switching in MacOSX to other applications with Apple-Tab.
+- Fixes measureText with empty string to be proper size.
+- Fixes for aliased text by applying linear filtering by default on Fonts.
+- Fixes RenderState::StateBlock::bindNoRestore() issue where blend function was not restored to the proper defaults.
+- Fixes some inconsistencies in Game event method names for menuEvent. (breaks compat. in Game)
+- Fixes some inconsistencies with AnimationClip::getID() to be same as Node::getId() and other classes. (breaks compat. in AnimationClip)
+- Fixes to gameplay-encoder and PhysicsRigidBody for heightmaps which adds height precision into generated heightmap png's.
+
+## v1.3.0
+
+- Portrait mode games on mobile platforms.
+- Fullscreen and configurable game resolutions on desktop platforms.
+- User Interface support for scrolling with scrollbars on Container.
+- PVRTC, ATC and DXT texture compression support.
+- Performance improvements in user interface forms and text.
+- Performance improvements in animations on transforms.
+- Performance improvements using NEON math for BlackBerry and iOS.
+- Fixes for improvements in error handling throughout all systems.
+- Fixes supporting built-in Maya COLLADA exporter via DAE_FBX export.
+- Fixes for latest FBX SDK 2013 support.
+- Fixes for loading from some WAV files that were crashing.
+- Fixes for From/By animations.
+- Fixes allowing all inline properties loaded within .scene files. (breaks compat. for .scene)
+- Fixes in .scene files for collisionObject definitions (breaks compat. for .scene)
+- Fixes for depth/z-ordering of controls.
+
+## v1.2.0
+
+- BlackBerry 10 support.
+- iOS 5.1 support.
+- Android 2.3+ support.
+- User interface system with declarative forms and themes.
+- Bluetooth keyboard/mouse support on BlackBerry platform.
+- Developer guide.
+- Sample/tutorial for sample03-character.
+- Sample for sample04-particles to demonstrate particle emitters.
+- Fixes for loading properties from URL.
+- Fixes on Win32/MacOSX for when mouse pointer leaves the window and returns.
+- Fixes to accelerometer for Android.
+- Fixes in animation blending.
+- Fixes to GPB for loading from single node and parent node. (breaks compat. for .gpb)
+
+## v1.1.0
+
+- FBX support in gameplay-encoder.
+- MacOSX platform support using XCode.
+- Off-screen rendering functionality using FrameBuffer.
+- Loading 3D scenes using declarative  .scene files.
+- Loading audio from .ogg files using vorbis.
+- Loading AudioSources from .audio files.
+- Loading Animations from .animation files.
+- AnimationClip support for cross fading.
+- Physics support using Bullet Physics.
+- Cross-platform new project generator.
+- Overloaded operators in Math classes.
+- Font improvements for justify, clip, wrap and scaling.
+- Fixes for Font::drawText to use point size and not float scalar.
+- Fixes for memory leaks in and fixes to AnimationTarget.
+- Fixes for bumped and parallax shaders.
+- Fixes to simplify folders for resources in samples.
+- Fixes to the material/shader system.
+- Fixes to the ParticleEmitter.
+
+## v1.0.0
+
+- Initial release.
+
+
+
+

+ 1 - 1
CMakeLists.txt

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.6)
 
 project(GamePlay)
 
-set(GAMEPLAY_VERSION 1.7.0)
+set(GAMEPLAY_VERSION 2.0.0)
 set(CMAKE_C_COMPILER_INIT g++)
 
 # debug

+ 41 - 42
README.md

@@ -1,42 +1,41 @@
-## 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. 
-
-<img align="right" src="https://raw.github.com/wiki/blackberry/GamePlay/img/logo.png" alt="gameplay" />
-
-- [Website](http://www.gameplay3d.org/)
-- [Forums](http://www.gameplay3d.org/forums/)
-- [Wiki](https://github.com/blackberry/GamePlay/wiki)
-- [API Reference](http://www.gameplay3d.org/api.php)
-- [Development Guide](https://github.com/blackberry/GamePlay/wiki#wiki-Development_Guide)
-
-## Supported Mobile Platforms
-- [BlackBerry 10 and PlayBook](https://github.com/blackberry/GamePlay/wiki/BlackBerry-Setup) (using BlackBerry Native SDK)
-- [Apple iOS 5+](https://github.com/blackberry/GamePlay/wiki/Apple-Xcode-Setup) (using Apple XCode 4)
-- [Google Android 2.3+](https://github.com/blackberry/GamePlay/wiki/Android-NDK-Setup) (using Google Android NDK)
-
-## Supported Desktop Platforms
-- [Microsoft Windows 7](https://github.com/blackberry/GamePlay/wiki/Visual-Studio-Setup) (using Microsoft Visual Studio 2010)
-- [Apple MacOS X](https://github.com/blackberry/GamePlay/wiki/Apple-Xcode-Setup) (using Apple XCode 4)
-- [Linux](https://github.com/blackberry/GamePlay/wiki/Linux-Setup) (using CMake)
-
-## Roadmap for 'next' branch
-- [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).
-
-## Bug Reporting
-Please log bugs under [Issues](https://github.com/blackberry/GamePlay/issues) on github.
-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 backlog issues list. 
-
-## 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.
+## GamePlay v2.0.0
+
+GamePlay3D is an open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
+
+<img align="right" src="https://raw.github.com/wiki/blackberry/GamePlay/img/logo.png" alt="gameplay3d" />
+
+- [Website](http://www.gameplay3d.org/)
+- [Forums](http://www.gameplay3d.org/forums/)
+- [Wiki](https://github.com/blackberry/GamePlay/wiki)
+- [API Reference](http://blackberry.github.io/GamePlay/api/index.html)
+- [Development Guide](https://github.com/blackberry/GamePlay/wiki#wiki-Development_Guide)
+
+## Supported Platforms
+- [Microsoft Windows](https://github.com/blackberry/GamePlay/wiki/Visual-Studio-Setup) (using Microsoft Visual Studio)
+- [Apple MacOS X](https://github.com/blackberry/GamePlay/wiki/Apple-Xcode-Setup) (using Apple XCode)
+- [Linux](https://github.com/blackberry/GamePlay/wiki/Linux-Setup) (using CMake)
+- [Apple iOS](https://github.com/blackberry/GamePlay/wiki/Apple-Xcode-Setup) (using Apple XCode)
+- [Google Android](https://github.com/blackberry/GamePlay/wiki/Android-NDK-Setup) (using Google Android NDK)
+- [BlackBerry](https://github.com/blackberry/GamePlay/wiki/BlackBerry-Setup) (using BlackBerry Native SDK)
+
+## Roadmap for 'next' branch
+- [3.0.0](https://github.com/blackberry/GamePlay/issues?milestone=7&page=1&state=open)
+- [backlog](https://github.com/blackberry/GamePlay/issues?milestone=5&page=1&state=open)
+
+## 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).
+
+## Bug Reporting
+Please log bugs under [Issues](https://github.com/blackberry/GamePlay/issues) on github.
+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 backlog issues list. 
+
+## 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.

+ 3 - 3
api/README.md

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

+ 1 - 1
gameplay.doxyfile

@@ -32,7 +32,7 @@ PROJECT_NAME           = gameplay
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.6.0
+PROJECT_NUMBER         = 2.0.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description 
 # for a project that appears at the top of each page and should give viewer 

+ 153 - 336
gameplay.sln

@@ -1,336 +1,153 @@
-
-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}") = "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}") = "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}") = "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}") = "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}") = "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}") = "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}") = "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}") = "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
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|BlackBerry = Debug|BlackBerry
-		Debug|BlackBerrySimulator = Debug|BlackBerrySimulator
-		Debug|Win32 = Debug|Win32
-		Debug|x64 = Debug|x64
-		DebugMem|BlackBerry = DebugMem|BlackBerry
-		DebugMem|BlackBerrySimulator = DebugMem|BlackBerrySimulator
-		DebugMem|Win32 = DebugMem|Win32
-		DebugMem|x64 = DebugMem|x64
-		Release|BlackBerry = Release|BlackBerry
-		Release|BlackBerrySimulator = Release|BlackBerrySimulator
-		Release|Win32 = Release|Win32
-		Release|x64 = Release|x64
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|Win32.ActiveCfg = Debug|Win32
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|Win32.Build.0 = Debug|Win32
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|x64.ActiveCfg = Debug|x64
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|x64.Build.0 = Debug|x64
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|x64.Build.0 = DebugMem|x64
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|Win32.ActiveCfg = Release|Win32
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|Win32.Build.0 = Release|Win32
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|x64.ActiveCfg = Release|x64
-		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|x64.Build.0 = Release|x64
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|Win32.ActiveCfg = Debug|Win32
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|Win32.Build.0 = Debug|Win32
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|x64.ActiveCfg = Debug|x64
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|x64.Build.0 = Debug|x64
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|x64.Build.0 = DebugMem|x64
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|Win32.ActiveCfg = Release|Win32
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|Win32.Build.0 = Release|Win32
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|x64.ActiveCfg = Release|x64
-		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|x64.Build.0 = Release|x64
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|Win32.ActiveCfg = Debug|Win32
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|Win32.Build.0 = Debug|Win32
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|x64.ActiveCfg = Debug|x64
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|x64.Build.0 = Debug|x64
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|x64.Build.0 = DebugMem|x64
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|Win32.ActiveCfg = Release|Win32
-		{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
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|Win32.ActiveCfg = Debug|Win32
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|Win32.Build.0 = Debug|Win32
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|x64.ActiveCfg = Debug|x64
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|x64.Build.0 = Debug|x64
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.DebugMem|x64.Build.0 = DebugMem|x64
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|Win32.ActiveCfg = Release|Win32
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|Win32.Build.0 = Release|Win32
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|x64.ActiveCfg = Release|x64
-		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Release|x64.Build.0 = Release|x64
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|Win32.ActiveCfg = Debug|Win32
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|Win32.Build.0 = Debug|Win32
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|x64.ActiveCfg = Debug|x64
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|x64.Build.0 = Debug|x64
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|x64.Build.0 = DebugMem|x64
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|Win32.ActiveCfg = Release|Win32
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|Win32.Build.0 = Release|Win32
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|x64.ActiveCfg = Release|x64
-		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|x64.Build.0 = Release|x64
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|Win32.ActiveCfg = Debug|Win32
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|Win32.Build.0 = Debug|Win32
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|x64.ActiveCfg = Debug|x64
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|x64.Build.0 = Debug|x64
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|x64.Build.0 = DebugMem|x64
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|Win32.ActiveCfg = Release|Win32
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|Win32.Build.0 = Release|Win32
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|x64.ActiveCfg = Release|x64
-		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|x64.Build.0 = Release|x64
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|Win32.ActiveCfg = Debug|Win32
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|Win32.Build.0 = Debug|Win32
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|x64.ActiveCfg = Debug|x64
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|x64.Build.0 = Debug|x64
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|x64.Build.0 = DebugMem|x64
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|Win32.ActiveCfg = Release|Win32
-		{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
-		{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
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|Win32.ActiveCfg = Debug|Win32
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|Win32.Build.0 = Debug|Win32
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|x64.ActiveCfg = Debug|x64
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|x64.Build.0 = Debug|x64
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|x64.Build.0 = DebugMem|x64
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|Win32.ActiveCfg = Release|Win32
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|Win32.Build.0 = Release|Win32
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|x64.ActiveCfg = Release|x64
-		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|x64.Build.0 = Release|x64
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.21005.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gameplay", "gameplay\gameplay.vcxproj", "{1032BA4B-57EB-4348-9E03-29DD63E80E4A}"
+EndProject
+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}") = "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}") = "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}") = "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}") = "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}") = "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}") = "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
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		DebugMem|Win32 = DebugMem|Win32
+		DebugMem|x64 = DebugMem|x64
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|Win32.ActiveCfg = Debug|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|Win32.Build.0 = Debug|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|x64.ActiveCfg = Debug|x64
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Debug|x64.Build.0 = Debug|x64
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.DebugMem|x64.Build.0 = DebugMem|x64
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|Win32.ActiveCfg = Release|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|Win32.Build.0 = Release|Win32
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|x64.ActiveCfg = Release|x64
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A}.Release|x64.Build.0 = Release|x64
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|Win32.ActiveCfg = Debug|Win32
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|Win32.Build.0 = Debug|Win32
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|x64.ActiveCfg = Debug|x64
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Debug|x64.Build.0 = Debug|x64
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.DebugMem|x64.Build.0 = DebugMem|x64
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|Win32.ActiveCfg = Release|Win32
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|Win32.Build.0 = Release|Win32
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|x64.ActiveCfg = Release|x64
+		{0F27C8C4-58B2-E367-8D1F-01B714FDBF1B}.Release|x64.Build.0 = Release|x64
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|Win32.ActiveCfg = Debug|Win32
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|Win32.Build.0 = Debug|Win32
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|x64.ActiveCfg = Debug|x64
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Debug|x64.Build.0 = Debug|x64
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.DebugMem|x64.Build.0 = DebugMem|x64
+		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|Win32.ActiveCfg = Release|Win32
+		{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|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|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|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
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|Win32.Build.0 = Debug|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|x64.ActiveCfg = Debug|x64
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Debug|x64.Build.0 = Debug|x64
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.DebugMem|x64.Build.0 = DebugMem|x64
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|Win32.ActiveCfg = Release|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|Win32.Build.0 = Release|Win32
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|x64.ActiveCfg = Release|x64
+		{CC37B8E9-6402-4841-8D6A-5D908A5909B3}.Release|x64.Build.0 = Release|x64
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|Win32.ActiveCfg = Debug|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|Win32.Build.0 = Debug|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|x64.ActiveCfg = Debug|x64
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Debug|x64.Build.0 = Debug|x64
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.DebugMem|x64.Build.0 = DebugMem|x64
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|Win32.ActiveCfg = Release|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|Win32.Build.0 = Release|Win32
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|x64.ActiveCfg = Release|x64
+		{87388E8B-F3CF-428F-BC2C-C1886248C111}.Release|x64.Build.0 = Release|x64
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|Win32.ActiveCfg = Debug|Win32
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|Win32.Build.0 = Debug|Win32
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|x64.ActiveCfg = Debug|x64
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Debug|x64.Build.0 = Debug|x64
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.DebugMem|x64.Build.0 = DebugMem|x64
+		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|Win32.ActiveCfg = Release|Win32
+		{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
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|Win32.ActiveCfg = Debug|Win32
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|Win32.Build.0 = Debug|Win32
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|x64.ActiveCfg = Debug|x64
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|x64.Build.0 = Debug|x64
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.DebugMem|x64.Build.0 = DebugMem|x64
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|Win32.ActiveCfg = Release|Win32
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|Win32.Build.0 = Release|Win32
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|x64.ActiveCfg = Release|x64
+		{82522888-E09A-ED48-AD7D-247237B37B3A}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 0 - 3
gameplay.xcworkspace/contents.xcworkspacedata

@@ -10,9 +10,6 @@
    <FileRef
       location = "group:samples/character/sample-character.xcodeproj">
    </FileRef>
-   <FileRef
-      location = "group:samples/longboard/sample-longboard.xcodeproj">
-   </FileRef>
    <FileRef
       location = "group:samples/lua/sample-lua.xcodeproj">
    </FileRef>  

+ 6 - 0
gameplay/.cproject

@@ -24,6 +24,7 @@
 								<option id="com.qnx.qcc.option.compiler.defines.1481323494" 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"/>
+									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.2133604142" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/lua/include&quot;"/>
@@ -87,6 +88,7 @@
 								<option id="com.qnx.qcc.option.compiler.defines.398688299" 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"/>
+									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1670164593" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
@@ -145,6 +147,8 @@
 								<option id="com.qnx.qcc.option.compiler.security.1671403331" 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.1863269886" 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"/>
+									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.847642559" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/bullet/include&quot;"/>
@@ -215,4 +219,6 @@
 	</storageModule>
 	<storageModule moduleId="com.qnx.tools.ide.qde.core.QNXProjectProperties"/>
 	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
+	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+	<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
 </cproject>

+ 44 - 24
gameplay/CMakeLists.txt

@@ -1,3 +1,4 @@
+
 set(GAMEPLAY_SRC
     src/AbsoluteLayout.cpp
     src/AbsoluteLayout.h
@@ -48,6 +49,8 @@ set(GAMEPLAY_SRC
     src/Container.h
     src/Control.cpp
     src/Control.h
+    src/ControlFactory.h
+    src/ControlFactory.cpp
     src/Curve.cpp
     src/Curve.h
     src/DebugNew.cpp
@@ -284,6 +287,8 @@ set(GAMEPLAY_LUA
     src/lua/lua_Button.h
     src/lua/lua_Camera.cpp
     src/lua/lua_Camera.h
+    src/lua/lua_CameraListener.h
+    src/lua/lua_CameraListener.cpp
     src/lua/lua_CameraType.cpp
     src/lua/lua_CameraType.h
     src/lua/lua_CheckBox.cpp
@@ -296,6 +301,8 @@ set(GAMEPLAY_LUA
     src/lua/lua_Control.h
     src/lua/lua_ControlAlignment.cpp
     src/lua/lua_ControlAlignment.h
+    src/lua/lua_ControlAutoSize.cpp
+    src/lua/lua_ControlAutoSize.h
     src/lua/lua_ControlListener.cpp
     src/lua/lua_ControlListener.h
     src/lua/lua_ControlListenerEventType.cpp
@@ -318,6 +325,8 @@ set(GAMEPLAY_LUA
     src/lua/lua_FlowLayout.h
     src/lua/lua_Font.cpp
     src/lua/lua_Font.h
+    src/lua/lua_FontFormat.cpp
+    src/lua/lua_FontFormat.h
     src/lua/lua_FontJustify.cpp
     src/lua/lua_FontJustify.h
     src/lua/lua_FontStyle.cpp
@@ -495,14 +504,18 @@ set(GAMEPLAY_LUA
     src/lua/lua_RenderStateCullFaceSide.cpp
     src/lua/lua_RenderStateDepthFunction.cpp
     src/lua/lua_RenderStateDepthFunction.h
+    src/lua/lua_RenderStateFrontFace.cpp
+    src/lua/lua_RenderStateFrontFace.h
     src/lua/lua_RenderStateStateBlock.cpp
     src/lua/lua_RenderStateStateBlock.h
+    src/lua/lua_RenderStateStencilFunction.cpp
+    src/lua/lua_RenderStateStencilFunction.h
+    src/lua/lua_RenderStateStencilOperation.cpp
+    src/lua/lua_RenderStateStencilOperation.h
     src/lua/lua_RenderTarget.cpp
     src/lua/lua_RenderTarget.h
     src/lua/lua_Scene.cpp
     src/lua/lua_Scene.h
-    src/lua/lua_SceneDebugFlags.cpp
-    src/lua/lua_SceneDebugFlags.h
     src/lua/lua_ScreenDisplayer.cpp
     src/lua/lua_ScreenDisplayer.h
     src/lua/lua_ScriptController.cpp
@@ -519,10 +532,12 @@ 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_TerrainPatch.cpp
+    src/lua/lua_TerrainPatch.h
     src/lua/lua_TextBox.cpp
     src/lua/lua_TextBox.h
+    src/lua/lua_TextBoxInputMode.cpp
+    src/lua/lua_TextBoxInputMode.h
     src/lua/lua_Texture.cpp
     src/lua/lua_Texture.h
     src/lua/lua_TextureFilter.cpp
@@ -579,44 +594,49 @@ set(GAMEPLAY_RES
 )
 
 set(GAMEPLAY_RES_SHADERS
-    res/shaders/colored-unlit.frag
-    res/shaders/colored-unlit.vert
     res/shaders/colored.frag
     res/shaders/colored.vert
-    res/shaders/textured-bumped.frag
-    res/shaders/textured-bumped.vert
-    res/shaders/textured-unlit.frag
-    res/shaders/textured-unlit.vert
-    res/shaders/textured.frag
-    res/shaders/textured.vert
+    res/shaders/font.frag
+    res/shaders/font.vert
+    res/shaders/form.frag
+    res/shaders/form.vert
+    res/shaders/lighting.frag
+    res/shaders/lighting.vert
+    res/shaders/skinning.vert
+    res/shaders/skinning-none.vert
+    res/shaders/sprite.frag
+    res/shaders/sprite.vert
     res/shaders/terrain.frag
     res/shaders/terrain.vert
+    res/shaders/textured.frag
+    res/shaders/textured.vert
 )
 
-set(GAMEPLAY_RES_SHADERS_LIB
-    res/shaders/lib/attributes-skinning.vert
-    res/shaders/lib/attributes.vert
-    res/shaders/lib/lighting-directional.frag
-    res/shaders/lib/lighting-directional.vert
-    res/shaders/lib/lighting-point.frag
-    res/shaders/lib/lighting-point.vert
-    res/shaders/lib/lighting-spot.frag
-    res/shaders/lib/lighting-spot.vert
-    res/shaders/lib/lighting.frag
+set(GAMEPLAY_RES_SHADERS
+    res/ui/arial.gpb
+    res/ui/default.png
+    res/ui/default.theme
 )
 
 include_directories( 
     src
     ../external-deps/lua/include
     ../external-deps/bullet/include
-    ../external-deps/libpng/include
+    ../external-deps/png/include
     ../external-deps/zlib/include
     ../external-deps/oggvorbis/include
     ../external-deps/openal/include
     ../external-deps/glew/include
 )
 
+IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK2 REQUIRED gtk+-2.0)
+include_directories(${GTK2_INCLUDE_DIRS})
+add_definitions(${GTK2_CFLAGS_OTHER})
 add_definitions(-D__linux__)
+ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+
 add_definitions(-lstdc++)
 
 add_library(gameplay STATIC
@@ -631,7 +651,7 @@ set_target_properties(gameplay PROPERTIES
 )
 
 source_group(lua FILES ${GAMEPLAY_LUA})
-source_group(res FILES ${GAMEPLAY_RES} ${GAMEPLAY_RES} ${GAMEPLAY_RES_SHADERS} ${GAMEPLAY_RES_SHADERS_LIB})
+source_group(res FILES ${GAMEPLAY_RES} ${GAMEPLAY_RES} ${GAMEPLAY_RES_SHADERS} ${GAMEPLAY_RES_UI})
 source_group(src FILES ${GAMEPLAY_SRC})
 
 

+ 2 - 2
gameplay/android/AndroidManifest.xml

@@ -2,9 +2,9 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="org.gameplay3d.gameplay"
     android:versionCode="1"
-    android:versionName="1.0" >
+    android:versionName="2.0" >
 
-    <uses-sdk android:minSdkVersion="9" />
+    <uses-sdk android:minSdkVersion="14" />
 
     <application
         android:icon="@drawable/ic_launcher"

+ 33 - 0
gameplay/android/java/.project

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>libgameplay3d</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

+ 9 - 0
gameplay/android/java/AndroidManifest.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.gameplay3d.lib"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="8"/>
+
+</manifest> 

+ 19 - 31
samples/longboard/android/build.xml → gameplay/android/java/build.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project name="sample-longboard" default="help">
+<project name="gameplay3dandroid" 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,36 +40,24 @@
     <loadproperties srcFile="project.properties" />
 
     <!-- quick check on sdk.dir -->
-    <fail message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
-          unless="sdk.dir" />
-
-
-<!-- extension targets. Uncomment the ones where you want to do custom work
-     in between standard targets -->
-    <target name="-pre-build">
-		<mkdir dir="src"/>
-    </target>
-	 
-<!--
-    <target name="-pre-compile">
-    </target>
-
-    /* This is typically used for code obfuscation.
-       Compiled code location: ${out.classes.absolute.dir}
-       If this is not done in place, override ${out.dex.input.absolute.dir} */
-       -->
-    <target name="-post-compile">
-        <copy file="../res/asphalt.png" tofile="assets/res/asphalt.png"/>
-		<copy file="../res/longboard.png" tofile="assets/res/longboard.png"/>
-        <copy file="../res/longboard.wav" tofile="assets/res/longboard.wav"/>
-        <copy file="../res/longboard_wheels.png" tofile="assets/res/longboard_wheels.png"/>
-        <copy file="../res/overlay_gradient.png" tofile="assets/res/overlay_gradient.png"/>
-        <copy file="../../../gameplay/res/logo_powered_white.png" tofile="assets/res/logo_powered_white.png"/>
-        <copy todir="assets/res/shaders">
-            <fileset dir="../../../gameplay/res/shaders"/>
-        </copy>
-    </target>
-
+    <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"
+    />
+
+    <!--
+        Import per project custom build rules if present at the root of the project.
+        This is the place to put custom intermediary targets such as:
+            -pre-build
+            -pre-compile
+            -post-compile (This is typically used for code obfuscation.
+                           Compiled code location: ${out.classes.absolute.dir}
+                           If this is not done in place, override ${out.dex.input.absolute.dir})
+            -post-package
+            -post-build
+            -pre-clean
+    -->
+    <import file="custom_rules.xml" optional="true" />
 
     <!-- Import the actual build file.
 

+ 188 - 0
gameplay/android/java/src/org/gameplay3d/GamePlayActivity.java

@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * 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.
+ */
+
+package org.gameplay3d;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import com.google.android.gms.appstate.AppStateClient;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.games.GamesClient;
+import com.google.android.gms.plus.PlusClient;
+
+/**
+ * Example base class for games. This implementation takes care of setting up
+ * the GamesClient object and managing its lifecycle. Subclasses only need to
+ * override the @link{#onSignInSucceeded} and @link{#onSignInFailed} abstract
+ * methods. To initiate the sign-in flow when the user clicks the sign-in
+ * button, subclasses should call @link{#beginUserInitiatedSignIn}. By default,
+ * this class only instantiates the GamesClient object. If the PlusClient or
+ * AppStateClient objects are also wanted, call the BaseGooglePlayActivity(int)
+ * constructor and specify the requested clients. For example, to request
+ * PlusClient and GamesClient, use BaseGooglePlayActivity(CLIENT_GAMES | CLIENT_PLUS).
+ * To request all available clients, use BaseGooglePlayActivity(CLIENT_ALL).
+ * Alternatively, you can also specify the requested clients via
+ * @link{#setRequestedClients}, but you must do so before @link{#onCreate}
+ * gets called, otherwise the call will have no effect.
+ *
+ * @author Bruno Oliveira (Google)
+ */
+public abstract class GooglePlayActivity extends FragmentActivity implements GooglePlayClientHelper.GamePlayClientHelperListener {
+
+    // The game helper object. This class is mainly a wrapper around this object.
+    protected GamePlayClientHelper mClient;
+
+    // We expose these constants here because we don't want users of this class
+    // to have to know about GamePlayClientHelper at all.
+    public static final int CLIENT_GAMES = GamePlayClientHelper.CLIENT_GAMES;
+    public static final int CLIENT_APPSTATE = GamePlayClientHelper.CLIENT_APPSTATE;
+    public static final int CLIENT_PLUS = GamePlayClientHelper.CLIENT_PLUS;
+    public static final int CLIENT_ALL = GamePlayClientHelper.CLIENT_ALL;
+
+    // Requested clients. By default, that's just the games client.
+    protected int mRequestedClients = CLIENT_GAMES;
+
+    // stores any additional scopes.
+    private String[] mAdditionalScopes;
+
+    protected String mDebugTag = "GooglePlayActivity";
+    protected boolean mDebugLog = false;
+
+    /** Constructs a BaseGooglePlayActivity with default client (GamesClient). */
+    protected GooglePlayActivity() {
+        super();
+        mClient = new GamePlayClientHelper(this);
+    }
+
+    /**
+     * Constructs a GooglePlayActivity with the requested clients.
+     * @param requestedClients The requested clients (a combination of CLIENT_GAMES,
+     *         CLIENT_PLUS and CLIENT_APPSTATE).
+     */
+    protected GooglePlayActivity(int requestedClients) {
+        super();
+        setRequestedClients(requestedClients);
+    }
+
+    /**
+     * Sets the requested clients. The preferred way to set the requested clients is
+     * via the constructor, but this method is available if for some reason your code
+     * cannot do this in the constructor. This must be called before onCreate in order to
+     * have any effect. If called after onCreate, this method is a no-op.
+     *
+     * @param requestedClients A combination of the flags CLIENT_GAMES, CLIENT_PLUS
+     *         and CLIENT_APPSTATE, or CLIENT_ALL to request all available clients.
+     * @param additionalScopes.  Scopes that should also be requested when the auth
+     *         request is made.
+     */
+    protected void setRequestedClients(int requestedClients, String ... additionalScopes) {
+        mRequestedClients = requestedClients;
+        mAdditionalScopes = additionalScopes;
+    }
+
+    @Override
+    protected void onCreate(Bundle b) {
+        super.onCreate(b);
+        mClient = new GamePlayClientHelper(this);
+        if (mDebugLog) {
+            mClient.enableDebugLog(mDebugLog, mDebugTag);
+        }
+        mClient.setup(this, mRequestedClients, mAdditionalScopes);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mClient.onStart(this);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mClient.onStop();
+    }
+
+    @Override
+    protected void onActivityResult(int request, int response, Intent data) {
+        super.onActivityResult(request, response, data);
+        mClient.onActivityResult(request, response, data);
+    }
+
+    protected GamesClient getGamesClient() {
+        return mClient.getGamesClient();
+    }
+
+    protected AppStateClient getAppStateClient() {
+        return mClient.getAppStateClient();
+    }
+
+    protected PlusClient getPlusClient() {
+        return mClient.getPlusClient();
+    }
+
+    protected boolean isSignedIn() {
+        return mClient.isSignedIn();
+    }
+
+    protected void beginUserInitiatedSignIn() {
+        mClient.beginUserInitiatedSignIn();
+    }
+
+    protected void signOut() {
+        mClient.signOut();
+    }
+
+    protected void showAlert(String title, String message) {
+        mClient.showAlert(title, message);
+    }
+
+    protected void showAlert(String message) {
+        mClient.showAlert(message);
+    }
+
+    protected void enableDebugLog(boolean enabled, String tag) {
+        mDebugLog = true;
+        mDebugTag = tag;
+        if (mClient != null) {
+            mClient.enableDebugLog(enabled, tag);
+        }
+    }
+
+    protected String getInvitationId() {
+        return mClient.getInvitationId();
+    }
+
+    protected void reconnectClients(int whichClients) {
+        mClient.reconnectClients(whichClients);
+    }
+
+    protected String getScopes() {
+        return mClient.getScopes();
+    }
+
+    protected String[] getScopesArray() {
+        return mClient.getScopesArray();
+    }
+
+    protected boolean hasSignInError() {
+        return mClient.hasSignInError();
+    }
+
+    protected GamePlayClientHelper.SignInFailureReason getSignInError() {
+        return mClient.getSignInError();
+    }
+}

+ 1135 - 0
gameplay/android/java/src/org/gameplay3d/GooglePlayClientHelper.java

@@ -0,0 +1,1135 @@
+
+package org.gameplay3d;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Vector;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+
+import com.google.android.gms.appstate.AppStateClient;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GooglePlayServicesClient;
+import com.google.android.gms.common.GooglePlayServicesUtil;
+import com.google.android.gms.common.Scopes;
+import com.google.android.gms.games.GamesActivityResultCodes;
+import com.google.android.gms.games.GamesClient;
+import com.google.android.gms.games.multiplayer.Invitation;
+import com.google.android.gms.plus.PlusClient;
+
+public class GooglePlayClientHelper implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener 
+{
+
+    /** Listener for sign-in success or failure events. */
+    public interface GooglePlayClientHelperListener 
+    {
+        /**
+         * Called when sign-in fails. As a result, a "Sign-In" button can be
+         * shown to the user; when that button is clicked, call
+         * @link{GamesHelper#beginUserInitiatedSignIn}. Note that not all calls to this
+         * method mean an error; it may be a result of the fact that automatic
+         * sign-in could not proceed because user interaction was required
+         * (consent dialogs). So implementations of this method should NOT
+         * display an error message unless a call to @link{GamesHelper#hasSignInError}
+         * indicates that an error indeed occurred.
+         */
+        void onSignInFailed();
+
+        /** Called when sign-in succeeds. */
+        void onSignInSucceeded();
+    }
+
+    // States we can be in
+    public static final int STATE_UNCONFIGURED = 0;
+    public static final int STATE_DISCONNECTED = 1;
+    public static final int STATE_CONNECTING = 2;
+    public static final int STATE_CONNECTED = 3;
+
+    // State names (for debug logging, etc)
+    public static final String[] STATE_NAMES = 
+    {
+            "UNCONFIGURED", "DISCONNECTED", "CONNECTING", "CONNECTED"
+    };
+
+    // State we are in right now
+    int mState = STATE_UNCONFIGURED;
+    // Are we expecting the result of a resolution flow?
+    boolean mExpectingResolution = false;
+
+    /**
+     * The Activity we are bound to. We need to keep a reference to the Activity
+     * because some games methods require an Activity (a Context won't do). We
+     * are careful not to leak these references: we release them on onStop().
+     */
+    Activity mActivity = null;
+
+    // OAuth scopes required for the clients. Initialized in setup().
+    String mScopes[];
+
+    // Request code we use when invoking other Activities to complete the
+    // sign-in flow.
+    final static int RC_RESOLVE = 9001;
+
+    // Request code when invoking Activities whose result we don't care about.
+    final static int RC_UNUSED = 9002;
+
+    // Client objects we manage. If a given client is not enabled, it is null.
+    GamesClient mGamesClient = null;
+    PlusClient mPlusClient = null;
+    AppStateClient mAppStateClient = null;
+
+    // What clients we manage (OR-able values, can be combined as flags)
+    public final static int CLIENT_NONE = 0x00;
+    public final static int CLIENT_GAMES = 0x01;
+    public final static int CLIENT_PLUS = 0x02;
+    public final static int CLIENT_APPSTATE = 0x04;
+    public final static int CLIENT_ALL = CLIENT_GAMES | CLIENT_PLUS | CLIENT_APPSTATE;
+
+    // What clients were requested? (bit flags)
+    int mRequestedClients = CLIENT_NONE;
+
+    // What clients are currently connected? (bit flags)
+    int mConnectedClients = CLIENT_NONE;
+
+    // What client are we currently connecting?
+    int mClientCurrentlyConnecting = CLIENT_NONE;
+
+    // Whether to automatically try to sign in on onStart().
+    boolean mAutoSignIn = true;
+
+    /*
+     * Whether user has specifically requested that the sign-in process begin.
+     * If mUserInitiatedSignIn is false, we're in the automatic sign-in attempt
+     * that we try once the Activity is started -- if true, then the user has
+     * already clicked a "Sign-In" button or something similar
+     */
+    boolean mUserInitiatedSignIn = false;
+
+    // The connection result we got from our last attempt to sign-in.
+    ConnectionResult mConnectionResult = null;
+
+    // The error that happened during sign-in.
+    SignInFailureReason mSignInFailureReason = null;
+
+    // Print debug logs?
+    boolean mDebugLog = false;
+    String mDebugTag = "GooglePlayClientHelper";
+
+    /*
+     * If we got an invitation id when we connected to the games client, it's
+     * here. Otherwise, it's null.
+     */
+    String mInvitationId;
+
+    // Listener
+    GooglePlayClientHelperListener mListener = null;
+
+    /**
+     * Construct a GooglePlayClientHelper object, initially tied to the given Activity.
+     * After constructing this object, call @link{setup} from the onCreate()
+     * method of your Activity.
+     */
+    public GooglePlayClientHelper(Activity activity) {
+        mActivity = activity;
+    }
+
+    static private final int TYPE_DEVELOPER_ERROR = 1001;
+    static private final int TYPE_GooglePlayClientHelper_BUG = 1002;
+    boolean checkState(int type, String operation, String warning, int... expectedStates) {
+        for (int expectedState : expectedStates) {
+            if (mState == expectedState) {
+                return true;
+            }
+        }
+        StringBuilder sb = new StringBuilder();
+        if (type == TYPE_DEVELOPER_ERROR) {
+            sb.append("GooglePlayClientHelper: you attempted an operation at an invalid. ");
+        } else {
+            sb.append("GooglePlayClientHelper: bug detected. Please report it at our bug tracker ");
+            sb.append("https://github.com/playgameservices/android-samples/issues. ");
+            sb.append("Please include the last couple hundred lines of logcat output ");
+            sb.append("and describe the operation that caused this. ");
+        }
+        sb.append("Explanation: ").append(warning);
+        sb.append("Operation: ").append(operation).append(". ");
+        sb.append("State: ").append(STATE_NAMES[mState]).append(". ");
+        if (expectedStates.length == 1) {
+            sb.append("Expected state: ").append(STATE_NAMES[expectedStates[0]]).append(".");
+        } else {
+            sb.append("Expected states:");
+            for (int expectedState : expectedStates) {
+                sb.append(" " ).append(STATE_NAMES[expectedState]);
+            }
+            sb.append(".");
+        }
+
+        logWarn(sb.toString());
+        return false;
+    }
+
+    void assertConfigured(String operation) {
+        if (mState == STATE_UNCONFIGURED) {
+            String error = "GooglePlayClientHelper error: Operation attempted without setup: " + operation +
+                    ". The setup() method must be called before attempting any other operation.";
+            logError(error);
+            throw new IllegalStateException(error);
+        }
+    }
+
+    /**
+     * Same as calling @link{setup(GooglePlayClientHelperListener, int)} requesting only the
+     * CLIENT_GAMES client.
+     */
+    public void setup(GooglePlayClientHelperListener listener) {
+        setup(listener, CLIENT_GAMES);
+    }
+
+    /**
+     * Performs setup on this GooglePlayClientHelper object. Call this from the onCreate()
+     * method of your Activity. This will create the clients and do a few other
+     * initialization tasks. Next, call @link{#onStart} from the onStart()
+     * method of your Activity.
+     *
+     * @param listener The listener to be notified of sign-in events.
+     * @param clientsToUse The clients to use. Use a combination of
+     *            CLIENT_GAMES, CLIENT_PLUS and CLIENT_APPSTATE, or CLIENT_ALL
+     *            to request all clients.
+     * @param additionalScopes Any scopes to be used that are outside of the ones defined
+     *            in the Scopes class.
+     *            I.E. for YouTube uploads one would add
+     *            "https://www.googleapis.com/auth/youtube.upload"
+     */
+    public void setup(GooglePlayClientHelperListener listener, int clientsToUse, String ... additionalScopes) {
+        if (mState != STATE_UNCONFIGURED) {
+            String error = "GooglePlayClientHelper: you called GooglePlayClientHelper.setup() twice. You can only call " +
+                    "it once.";
+            logError(error);
+            throw new IllegalStateException(error);
+        }
+        mListener = listener;
+        mRequestedClients = clientsToUse;
+
+        debugLog("Setup: requested clients: " + mRequestedClients);
+
+        Vector<String> scopesVector = new Vector<String>();
+        if (0 != (clientsToUse & CLIENT_GAMES)) {
+            scopesVector.add(Scopes.GAMES);
+        }
+        if (0 != (clientsToUse & CLIENT_PLUS)) {
+            scopesVector.add(Scopes.PLUS_LOGIN);
+        }
+        if (0 != (clientsToUse & CLIENT_APPSTATE)) {
+            scopesVector.add(Scopes.APP_STATE);
+        }
+
+        if (null != additionalScopes) {
+            for (String scope : additionalScopes) {
+                scopesVector.add(scope);
+            }
+        }
+
+        mScopes = new String[scopesVector.size()];
+        scopesVector.copyInto(mScopes);
+
+        debugLog("setup: scopes:");
+        for (String scope : mScopes) {
+            debugLog("  - " + scope);
+        }
+
+        if (0 != (clientsToUse & CLIENT_GAMES)) {
+            debugLog("setup: creating GamesClient");
+            mGamesClient = new GamesClient.Builder(getContext(), this, this)
+                    .setGravityForPopups(Gravity.TOP | Gravity.CENTER_HORIZONTAL)
+                    .setScopes(mScopes)
+                    .create();
+        }
+
+        if (0 != (clientsToUse & CLIENT_PLUS)) {
+            debugLog("setup: creating GamesPlusClient");
+            mPlusClient = new PlusClient.Builder(getContext(), this, this)
+                    .setScopes(mScopes)
+                    .build();
+        }
+
+        if (0 != (clientsToUse & CLIENT_APPSTATE)) {
+            debugLog("setup: creating AppStateClient");
+            mAppStateClient = new AppStateClient.Builder(getContext(), this, this)
+                    .setScopes(mScopes)
+                    .create();
+        }
+        setState(STATE_DISCONNECTED);
+    }
+
+    void setState(int newState) {
+        String oldStateName = STATE_NAMES[mState];
+        String newStateName = STATE_NAMES[newState];
+        mState = newState;
+        debugLog("State change " + oldStateName + " -> " + newStateName);
+    }
+
+    /**
+     * Returns the GamesClient object. In order to call this method, you must have
+     * called @link{setup} with a set of clients that includes CLIENT_GAMES.
+     */
+    public GamesClient getGamesClient() {
+        if (mGamesClient == null) {
+            throw new IllegalStateException("No GamesClient. Did you request it at setup?");
+        }
+        return mGamesClient;
+    }
+
+    /**
+     * Returns the AppStateClient object. In order to call this method, you must have
+     * called @link{#setup} with a set of clients that includes CLIENT_APPSTATE.
+     */
+    public AppStateClient getAppStateClient() {
+        if (mAppStateClient == null) {
+            throw new IllegalStateException("No AppStateClient. Did you request it at setup?");
+        }
+        return mAppStateClient;
+    }
+
+    /**
+     * Returns the PlusClient object. In order to call this method, you must have
+     * called @link{#setup} with a set of clients that includes CLIENT_PLUS.
+     */
+    public PlusClient getPlusClient() {
+        if (mPlusClient == null) {
+            throw new IllegalStateException("No PlusClient. Did you request it at setup?");
+        }
+        return mPlusClient;
+    }
+
+    /** Returns whether or not the user is signed in. */
+    public boolean isSignedIn() {
+        return mState == STATE_CONNECTED;
+    }
+
+    /**
+     * Returns whether or not there was a (non-recoverable) error during the
+     * sign-in process.
+     */
+    public boolean hasSignInError() {
+        return mSignInFailureReason != null;
+    }
+
+    /**
+     * Returns the error that happened during the sign-in process, null if no
+     * error occurred.
+     */
+    public SignInFailureReason getSignInError() {
+        return mSignInFailureReason;
+    }
+
+    /** Call this method from your Activity's onStart(). */
+    public void onStart(Activity act) {
+        mActivity = act;
+
+        debugLog("onStart, state = " + STATE_NAMES[mState]);
+        assertConfigured("onStart");
+
+        switch (mState) {
+            case STATE_DISCONNECTED:
+                // we are not connected, so attempt to connect
+                if (mAutoSignIn) {
+                    debugLog("onStart: Now connecting clients.");
+                    startConnections();
+                } else {
+                    debugLog("onStart: Not connecting (user specifically signed out).");
+                }
+                break;
+            case STATE_CONNECTING:
+                // connection process is in progress; no action required
+                debugLog("onStart: connection process in progress, no action taken.");
+                break;
+            case STATE_CONNECTED:
+                // already connected (for some strange reason). No complaints :-)
+                debugLog("onStart: already connected (unusual, but ok).");
+                break;
+            default:
+                String msg =  "onStart: BUG: unexpected state " + STATE_NAMES[mState];
+                logError(msg);
+                throw new IllegalStateException(msg);
+        }
+    }
+
+    /** Call this method from your Activity's onStop(). */
+    public void onStop() {
+        debugLog("onStop, state = " + STATE_NAMES[mState]);
+        assertConfigured("onStop");
+        switch (mState) {
+            case STATE_CONNECTED:
+            case STATE_CONNECTING:
+                // kill connections
+                debugLog("onStop: Killing connections");
+                killConnections();
+                break;
+            case STATE_DISCONNECTED:
+                debugLog("onStop: not connected, so no action taken.");
+                break;
+            default:
+                String msg =  "onStop: BUG: unexpected state " + STATE_NAMES[mState];
+                logError(msg);
+                throw new IllegalStateException(msg);
+        }
+
+        // let go of the Activity reference
+        mActivity = null;
+    }
+
+    /** Convenience method to show an alert dialog. */
+    public void showAlert(String title, String message) {
+        (new AlertDialog.Builder(getContext())).setTitle(title).setMessage(message)
+                .setNeutralButton(android.R.string.ok, null).create().show();
+    }
+
+    /** Convenience method to show an alert dialog. */
+    public void showAlert(String message) {
+        (new AlertDialog.Builder(getContext())).setMessage(message)
+                .setNeutralButton(android.R.string.ok, null).create().show();
+    }
+
+    /**
+     * Returns the invitation ID received through an invitation notification.
+     * This should be called from your GooglePlayClientHelperListener's
+     *
+     * @link{GooglePlayClientHelperListener#onSignInSucceeded} method, to check if there's an
+     * invitation available. In that case, accept the invitation.
+     * @return The id of the invitation, or null if none was received.
+     */
+    public String getInvitationId() {
+        if (!checkState(TYPE_DEVELOPER_ERROR, "getInvitationId",
+                "Invitation ID is only available when connected " +
+                "(after getting the onSignInSucceeded callback).", STATE_CONNECTED)) {
+            return null;
+        }
+        return mInvitationId;
+    }
+
+    /** Enables debug logging */
+    public void enableDebugLog(boolean enabled, String tag) {
+        mDebugLog = enabled;
+        mDebugTag = tag;
+        if (enabled) {
+            debugLog("Debug log enabled, tag: " + tag);
+        }
+    }
+
+    /**
+     * Returns the current requested scopes. This is not valid until setup() has
+     * been called.
+     *
+     * @return the requested scopes, including the oauth2: prefix
+     */
+    public String getScopes() {
+        StringBuilder scopeStringBuilder = new StringBuilder();
+        if (null != mScopes) {
+            for (String scope: mScopes) {
+                addToScope(scopeStringBuilder, scope);
+            }
+        }
+        return scopeStringBuilder.toString();
+    }
+
+    /**
+     * Returns an array of the current requested scopes. This is not valid until
+     * setup() has been called
+     *
+     * @return the requested scopes, including the oauth2: prefix
+     */
+    public String[] getScopesArray() {
+        return mScopes;
+    }
+
+    /** Sign out and disconnect from the APIs. */
+    public void signOut() {
+        if (mState == STATE_DISCONNECTED) {
+            // nothing to do
+            debugLog("signOut: state was already DISCONNECTED, ignoring.");
+            return;
+        }
+
+        // for the PlusClient, "signing out" means clearing the default account and
+        // then disconnecting
+        if (mPlusClient != null && mPlusClient.isConnected()) {
+            debugLog("Clearing default account on PlusClient.");
+            mPlusClient.clearDefaultAccount();
+        }
+
+        // For the games client, signing out means calling signOut and disconnecting
+        if (mGamesClient != null && mGamesClient.isConnected()) {
+            debugLog("Signing out from GamesClient.");
+            mGamesClient.signOut();
+        }
+
+        // Ready to disconnect
+        debugLog("Proceeding with disconnection.");
+        killConnections();
+    }
+
+    void killConnections() {
+        if (!checkState(TYPE_GooglePlayClientHelper_BUG, "killConnections", "killConnections() should only " +
+                "get called while connected or connecting.", STATE_CONNECTED, STATE_CONNECTING)) {
+            return;
+        }
+        debugLog("killConnections: killing connections.");
+
+        mConnectionResult = null;
+        mSignInFailureReason = null;
+
+        if (mGamesClient != null && mGamesClient.isConnected()) {
+            debugLog("Disconnecting GamesClient.");
+            mGamesClient.disconnect();
+        }
+        if (mPlusClient != null && mPlusClient.isConnected()) {
+            debugLog("Disconnecting PlusClient.");
+            mPlusClient.disconnect();
+        }
+        if (mAppStateClient != null && mAppStateClient.isConnected()) {
+            debugLog("Disconnecting AppStateClient.");
+            mAppStateClient.disconnect();
+        }
+        mConnectedClients = CLIENT_NONE;
+        debugLog("killConnections: all clients disconnected.");
+        setState(STATE_DISCONNECTED);
+    }
+
+    static String activityResponseCodeToString(int respCode) {
+        switch (respCode) {
+            case Activity.RESULT_OK:
+                return "RESULT_OK";
+            case Activity.RESULT_CANCELED:
+                return "RESULT_CANCELED";
+            case GamesActivityResultCodes.RESULT_APP_MISCONFIGURED:
+                return "RESULT_APP_MISCONFIGURED";
+            case GamesActivityResultCodes.RESULT_LEFT_ROOM:
+                return "RESULT_LEFT_ROOM";
+            case GamesActivityResultCodes.RESULT_LICENSE_FAILED:
+                return "RESULT_LICENSE_FAILED";
+            case GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED:
+                return "RESULT_RECONNECT_REQUIRED";
+            case GamesActivityResultCodes.RESULT_SIGN_IN_FAILED:
+                return "SIGN_IN_FAILED";
+            default:
+                return String.valueOf(respCode);
+        }
+    }
+
+    /**
+     * Handle activity result. Call this method from your Activity's
+     * onActivityResult callback. If the activity result pertains to the sign-in
+     * process, processes it appropriately.
+     */
+    public void onActivityResult(int requestCode, int responseCode, Intent intent) {
+        debugLog("onActivityResult: req=" + (requestCode == RC_RESOLVE ? "RC_RESOLVE" :
+                String.valueOf(requestCode)) + ", resp=" +
+                activityResponseCodeToString(responseCode));
+        if (requestCode != RC_RESOLVE) {
+            debugLog("onActivityResult: request code not meant for us. Ignoring.");
+            return;
+        }
+
+        // no longer expecting a resolution
+        mExpectingResolution = false;
+
+        if (mState != STATE_CONNECTING) {
+            debugLog("onActivityResult: ignoring because state isn't STATE_CONNECTING (" +
+                    "it's " + STATE_NAMES[mState] + ")");
+            return;
+        }
+
+        // We're coming back from an activity that was launched to resolve a
+        // connection problem. For example, the sign-in UI.
+        if (responseCode == Activity.RESULT_OK) {
+            // Ready to try to connect again.
+            debugLog("onAR: Resolution was RESULT_OK, so connecting current client again.");
+            connectCurrentClient();
+        } else if (responseCode == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED) {
+            debugLog("onAR: Resolution was RECONNECT_REQUIRED, so reconnecting.");
+            connectCurrentClient();
+        } else if (responseCode == Activity.RESULT_CANCELED) {
+            // User cancelled.
+            debugLog("onAR: Got a cancellation result, so disconnecting.");
+            mAutoSignIn = false;
+            mUserInitiatedSignIn = false;
+            mSignInFailureReason = null; // cancelling is not a failure!
+            killConnections();
+            notifyListener(false);
+        } else {
+            // Whatever the problem we were trying to solve, it was not
+            // solved. So give up and show an error message.
+            debugLog("onAR: responseCode="  + activityResponseCodeToString(responseCode) +
+                        ", so giving up.");
+            giveUp(new SignInFailureReason(mConnectionResult.getErrorCode(), responseCode));
+        }
+    }
+
+    void notifyListener(boolean success) {
+        debugLog("Notifying LISTENER of sign-in " + (success ? "SUCCESS" :
+                mSignInFailureReason != null ? "FAILURE (error)" : "FAILURE (no error)"));
+        if (mListener != null) {
+            if (success) {
+                mListener.onSignInSucceeded();
+            } else {
+                mListener.onSignInFailed();
+            }
+        }
+    }
+
+    /**
+     * Starts a user-initiated sign-in flow. This should be called when the user
+     * clicks on a "Sign In" button. As a result, authentication/consent dialogs
+     * may show up. At the end of the process, the GooglePlayClientHelperListener's
+     * onSignInSucceeded() or onSignInFailed() methods will be called.
+     */
+    public void beginUserInitiatedSignIn() {
+        if (mState == STATE_CONNECTED) {
+            // nothing to do
+            logWarn("beginUserInitiatedSignIn() called when already connected. " +
+                    "Calling listener directly to notify of success.");
+            notifyListener(true);
+            return;
+        } else if (mState == STATE_CONNECTING) {
+            logWarn("beginUserInitiatedSignIn() called when already connecting. " +
+                    "Be patient! You can only call this method after you get an " +
+                    "onSignInSucceeded() or onSignInFailed() callback. Suggestion: disable " +
+                    "the sign-in button on startup and also when it's clicked, and re-enable " +
+                    "when you get the callback.");
+            // ignore call (listener will get a callback when the connection process finishes)
+            return;
+        }
+
+        debugLog("Starting USER-INITIATED sign-in flow.");
+
+        // sign in automatically on onStart()
+        mAutoSignIn = true;
+
+        // Is Google Play services available?
+        int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getContext());
+        debugLog("isGooglePlayServicesAvailable returned " + result);
+        if (result != ConnectionResult.SUCCESS) {
+            // Google Play services is not available.
+            debugLog("Google Play services not available. Show error dialog.");
+            mSignInFailureReason = new SignInFailureReason(result, 0);
+            showFailureDialog();
+            notifyListener(false);
+            return;
+        }
+
+        // indicate that user is actively trying to sign in (so we know to resolve
+        // connection problems by showing dialogs)
+        mUserInitiatedSignIn = true;
+
+        if (mConnectionResult != null) {
+            // We have a pending connection result from a previous failure, so
+            // start with that.
+            debugLog("beginUserInitiatedSignIn: continuing pending sign-in flow.");
+            setState(STATE_CONNECTING);
+            resolveConnectionResult();
+        } else {
+            // We don't have a pending connection result, so start anew.
+            debugLog("beginUserInitiatedSignIn: starting new sign-in flow.");
+            startConnections();
+        }
+    }
+
+    Context getContext() {
+        return mActivity;
+    }
+
+    void addToScope(StringBuilder scopeStringBuilder, String scope) {
+        if (scopeStringBuilder.length() == 0) {
+            scopeStringBuilder.append("oauth2:");
+        } else {
+            scopeStringBuilder.append(" ");
+        }
+        scopeStringBuilder.append(scope);
+    }
+
+    void startConnections() {
+        if (!checkState(TYPE_GooglePlayClientHelper_BUG, "startConnections", "startConnections should " +
+                "only get called when disconnected.", STATE_DISCONNECTED)) {
+            return;
+        }
+        debugLog("Starting connections.");
+        setState(STATE_CONNECTING);
+        mInvitationId = null;
+        connectNextClient();
+    }
+
+    void connectNextClient() {
+        // do we already have all the clients we need?
+        debugLog("connectNextClient: requested clients: " + mRequestedClients +
+                    ", connected clients: " + mConnectedClients);
+
+        // failsafe, in case we somehow lost track of what clients are connected or not.
+        if (mGamesClient != null && mGamesClient.isConnected() &&
+                (0 == (mConnectedClients & CLIENT_GAMES))) {
+            logWarn("GamesClient was already connected. Fixing.");
+            mConnectedClients |= CLIENT_GAMES;
+        }
+        if (mPlusClient != null && mPlusClient.isConnected() &&
+                (0 == (mConnectedClients & CLIENT_PLUS))) {
+            logWarn("PlusClient was already connected. Fixing.");
+            mConnectedClients |= CLIENT_PLUS;
+        }
+        if (mAppStateClient != null && mAppStateClient.isConnected() &&
+                (0 == (mConnectedClients & CLIENT_APPSTATE))) {
+            logWarn("AppStateClient was already connected. Fixing");
+            mConnectedClients |= CLIENT_APPSTATE;
+        }
+
+        int pendingClients = mRequestedClients & ~mConnectedClients;
+        debugLog("Pending clients: " + pendingClients);
+
+        if (pendingClients == 0) {
+            debugLog("All clients now connected. Sign-in successful!");
+            succeedSignIn();
+            return;
+        }
+
+        // which client should be the next one to connect?
+        if (mGamesClient != null && (0 != (pendingClients & CLIENT_GAMES))) {
+            debugLog("Connecting GamesClient.");
+            mClientCurrentlyConnecting = CLIENT_GAMES;
+        } else if (mPlusClient != null && (0 != (pendingClients & CLIENT_PLUS))) {
+            debugLog("Connecting PlusClient.");
+            mClientCurrentlyConnecting = CLIENT_PLUS;
+        } else if (mAppStateClient != null && (0 != (pendingClients & CLIENT_APPSTATE))) {
+            debugLog("Connecting AppStateClient.");
+            mClientCurrentlyConnecting = CLIENT_APPSTATE;
+        } else {
+            // hmmm, getting here would be a bug.
+            throw new AssertionError("Not all clients connected, yet no one is next. R="
+                    + mRequestedClients + ", C=" + mConnectedClients);
+        }
+
+        connectCurrentClient();
+    }
+
+    void connectCurrentClient() {
+        if (mState == STATE_DISCONNECTED) {
+            // we got disconnected during the connection process, so abort
+            logWarn("GooglePlayClientHelper got disconnected during connection process. Aborting.");
+            return;
+        }
+        if (!checkState(TYPE_GooglePlayClientHelper_BUG, "connectCurrentClient", "connectCurrentClient " +
+                "should only get called when connecting.", STATE_CONNECTING)) {
+            return;
+        }
+
+        switch (mClientCurrentlyConnecting) {
+            case CLIENT_GAMES:
+                mGamesClient.connect();
+                break;
+            case CLIENT_APPSTATE:
+                mAppStateClient.connect();
+                break;
+            case CLIENT_PLUS:
+                mPlusClient.connect();
+                break;
+        }
+    }
+
+    /**
+     * Disconnects the indicated clients, then connects them again.
+     * @param whatClients Indicates which clients to reconnect.
+     */
+    public void reconnectClients(int whatClients) {
+        checkState(TYPE_DEVELOPER_ERROR, "reconnectClients", "reconnectClients should " +
+                "only be called when connected. Proceeding anyway.", STATE_CONNECTED);
+        boolean actuallyReconnecting = false;
+
+        if ((whatClients & CLIENT_GAMES) != 0 && mGamesClient != null
+                && mGamesClient.isConnected()) {
+            debugLog("Reconnecting GamesClient.");
+            actuallyReconnecting = true;
+            mConnectedClients &= ~CLIENT_GAMES;
+            mGamesClient.reconnect();
+        }
+        if ((whatClients & CLIENT_APPSTATE) != 0 && mAppStateClient != null
+                && mAppStateClient.isConnected()) {
+            debugLog("Reconnecting AppStateClient.");
+            actuallyReconnecting = true;
+            mConnectedClients &= ~CLIENT_APPSTATE;
+            mAppStateClient.reconnect();
+        }
+        if ((whatClients & CLIENT_PLUS) != 0 && mPlusClient != null
+                && mPlusClient.isConnected()) {
+            // PlusClient doesn't need reconnections.
+            logWarn("GooglePlayClientHelper is ignoring your request to reconnect " +
+                    "PlusClient because this is unnecessary.");
+        }
+
+        if (actuallyReconnecting) {
+            setState(STATE_CONNECTING);
+        } else {
+            // No reconnections are to take place, so for consistency we call the listener
+            // as if sign in had just succeeded.
+            debugLog("No reconnections needed, so behaving as if sign in just succeeded");
+            notifyListener(true);
+        }
+    }
+
+    /** Called when we successfully obtain a connection to a client. */
+    @Override
+    public void onConnected(Bundle connectionHint) {
+        debugLog("onConnected: connected! client=" + mClientCurrentlyConnecting);
+
+        // Mark the current client as connected
+        mConnectedClients |= mClientCurrentlyConnecting;
+        debugLog("Connected clients updated to: " + mConnectedClients);
+
+        // If this was the games client and it came with an invite, store it for
+        // later retrieval.
+        if (mClientCurrentlyConnecting == CLIENT_GAMES && connectionHint != null) {
+            debugLog("onConnected: connection hint provided. Checking for invite.");
+            Invitation inv = connectionHint.getParcelable(GamesClient.EXTRA_INVITATION);
+            if (inv != null && inv.getInvitationId() != null) {
+                // accept invitation
+                debugLog("onConnected: connection hint has a room invite!");
+                mInvitationId = inv.getInvitationId();
+                debugLog("Invitation ID: " + mInvitationId);
+            }
+        }
+
+        // connect the next client in line, if any.
+        connectNextClient();
+    }
+
+    void succeedSignIn() {
+        checkState(TYPE_GooglePlayClientHelper_BUG, "succeedSignIn", "succeedSignIn should only " +
+                "get called in the connecting or connected state. Proceeding anyway.",
+                STATE_CONNECTING, STATE_CONNECTED);
+        debugLog("All requested clients connected. Sign-in succeeded!");
+        setState(STATE_CONNECTED);
+        mSignInFailureReason = null;
+        mAutoSignIn = true;
+        mUserInitiatedSignIn = false;
+        notifyListener(true);
+    }
+
+    /** Handles a connection failure reported by a client. */
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+        // save connection result for later reference
+        debugLog("onConnectionFailed");
+
+        mConnectionResult = result;
+        debugLog("Connection failure:");
+        debugLog("   - code: " +  errorCodeToString(mConnectionResult.getErrorCode()));
+        debugLog("   - resolvable: " + mConnectionResult.hasResolution());
+        debugLog("   - details: " + mConnectionResult.toString());
+
+        if (!mUserInitiatedSignIn) {
+            // If the user didn't initiate the sign-in, we don't try to resolve
+            // the connection problem automatically -- instead, we fail and wait
+            // for the user to want to sign in. That way, they won't get an
+            // authentication (or other) popup unless they are actively trying
+            // to
+            // sign in.
+            debugLog("onConnectionFailed: since user didn't initiate sign-in, failing now.");
+            mConnectionResult = result;
+            setState(STATE_DISCONNECTED);
+            notifyListener(false);
+            return;
+        }
+
+        debugLog("onConnectionFailed: since user initiated sign-in, resolving problem.");
+
+        // Resolve the connection result. This usually means showing a dialog or
+        // starting an Activity that will allow the user to give the appropriate
+        // consents so that sign-in can be successful.
+        resolveConnectionResult();
+    }
+
+    /**
+     * Attempts to resolve a connection failure. This will usually involve
+     * starting a UI flow that lets the user give the appropriate consents
+     * necessary for sign-in to work.
+     */
+    void resolveConnectionResult() {
+        // Try to resolve the problem
+        checkState(TYPE_GooglePlayClientHelper_BUG, "resolveConnectionResult",
+                "resolveConnectionResult should only be called when connecting. Proceeding anyway.",
+                STATE_CONNECTING);
+
+        if (mExpectingResolution) {
+            debugLog("We're already expecting the result of a previous resolution.");
+            return;
+        }
+
+        debugLog("resolveConnectionResult: trying to resolve result: " + mConnectionResult);
+        if (mConnectionResult.hasResolution()) {
+            // This problem can be fixed. So let's try to fix it.
+            debugLog("Result has resolution. Starting it.");
+            try {
+                // launch appropriate UI flow (which might, for example, be the
+                // sign-in flow)
+                mExpectingResolution = true;
+                mConnectionResult.startResolutionForResult(mActivity, RC_RESOLVE);
+            } catch (SendIntentException e) {
+                // Try connecting again
+                debugLog("SendIntentException, so connecting again.");
+                connectCurrentClient();
+            }
+        } else {
+            // It's not a problem what we can solve, so give up and show an
+            // error.
+            debugLog("resolveConnectionResult: result has no resolution. Giving up.");
+            giveUp(new SignInFailureReason(mConnectionResult.getErrorCode()));
+        }
+    }
+
+    /**
+     * Give up on signing in due to an error. Shows the appropriate error
+     * message to the user, using a standard error dialog as appropriate to the
+     * cause of the error. That dialog will indicate to the user how the problem
+     * can be solved (for example, re-enable Google Play Services, upgrade to a
+     * new version, etc).
+     */
+    void giveUp(SignInFailureReason reason) {
+        checkState(TYPE_GooglePlayClientHelper_BUG, "giveUp", "giveUp should only be called when " +
+                "connecting. Proceeding anyway.", STATE_CONNECTING);
+        mAutoSignIn = false;
+        killConnections();
+        mSignInFailureReason = reason;
+        showFailureDialog();
+        notifyListener(false);
+    }
+
+    /** Called when we are disconnected from a client. */
+    @Override
+    public void onDisconnected() {
+        debugLog("onDisconnected.");
+        if (mState == STATE_DISCONNECTED) {
+            // This is expected.
+            debugLog("onDisconnected is expected, so no action taken.");
+            return;
+        }
+
+        // Unexpected disconnect (rare!)
+        logWarn("Unexpectedly disconnected. Severing remaining connections.");
+
+        // kill the other connections too, and revert to DISCONNECTED state.
+        killConnections();
+        mSignInFailureReason = null;
+
+        // call the sign in failure callback
+        debugLog("Making extraordinary call to onSignInFailed callback");
+        notifyListener(false);
+    }
+
+    /** Shows an error dialog that's appropriate for the failure reason. */
+    void showFailureDialog() {
+        Context ctx = getContext();
+        if (ctx == null) {
+            debugLog("*** No context. Can't show failure dialog.");
+            return;
+        }
+        debugLog("Making error dialog for failure: " + mSignInFailureReason);
+        Dialog errorDialog = null;
+        int errorCode = mSignInFailureReason.getServiceErrorCode();
+        int actResp = mSignInFailureReason.getActivityResultCode();
+
+        switch (actResp) {
+            case GamesActivityResultCodes.RESULT_APP_MISCONFIGURED:
+                errorDialog = makeSimpleDialog(ctx.getString(
+                        R.string.GooglePlayClientHelper_app_misconfigured));
+                printMisconfiguredDebugInfo();
+                break;
+            case GamesActivityResultCodes.RESULT_SIGN_IN_FAILED:
+                errorDialog = makeSimpleDialog(ctx.getString(
+                        R.string.GooglePlayClientHelper_sign_in_failed));
+                break;
+            case GamesActivityResultCodes.RESULT_LICENSE_FAILED:
+                errorDialog = makeSimpleDialog(ctx.getString(
+                        R.string.GooglePlayClientHelper_license_failed));
+                break;
+            default:
+                // No meaningful Activity response code, so generate default Google
+                // Play services dialog
+                errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode, mActivity,
+                        RC_UNUSED, null);
+                if (errorDialog == null) {
+                    // get fallback dialog
+                    debugLog("No standard error dialog available. Making fallback dialog.");
+                    errorDialog = makeSimpleDialog(ctx.getString(R.string.GooglePlayClientHelper_unknown_error)
+                            + " " + errorCodeToString(errorCode));
+                }
+        }
+
+        debugLog("Showing error dialog.");
+        errorDialog.show();
+    }
+
+    Dialog makeSimpleDialog(String text) {
+        return (new AlertDialog.Builder(getContext())).setMessage(text)
+                .setNeutralButton(android.R.string.ok, null).create();
+    }
+
+    void debugLog(String message) {
+        if (mDebugLog) {
+            Log.d(mDebugTag, "GooglePlayClientHelper: " + message);
+        }
+    }
+
+    void logWarn(String message) {
+        Log.w(mDebugTag, "!!! GooglePlayClientHelper WARNING: " + message);
+    }
+
+    void logError(String message) {
+        Log.e(mDebugTag, "*** GooglePlayClientHelper ERROR: " + message);
+    }
+
+    static String errorCodeToString(int errorCode) {
+        switch (errorCode) {
+            case ConnectionResult.DEVELOPER_ERROR:
+                return "DEVELOPER_ERROR(" + errorCode + ")";
+            case ConnectionResult.INTERNAL_ERROR:
+                return "INTERNAL_ERROR(" + errorCode + ")";
+            case ConnectionResult.INVALID_ACCOUNT:
+                return "INVALID_ACCOUNT(" + errorCode + ")";
+            case ConnectionResult.LICENSE_CHECK_FAILED:
+                return "LICENSE_CHECK_FAILED(" + errorCode + ")";
+            case ConnectionResult.NETWORK_ERROR:
+                return "NETWORK_ERROR(" + errorCode + ")";
+            case ConnectionResult.RESOLUTION_REQUIRED:
+                return "RESOLUTION_REQUIRED(" + errorCode + ")";
+            case ConnectionResult.SERVICE_DISABLED:
+                return "SERVICE_DISABLED(" + errorCode + ")";
+            case ConnectionResult.SERVICE_INVALID:
+                return "SERVICE_INVALID(" + errorCode + ")";
+            case ConnectionResult.SERVICE_MISSING:
+                return "SERVICE_MISSING(" + errorCode + ")";
+            case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
+                return "SERVICE_VERSION_UPDATE_REQUIRED(" + errorCode + ")";
+            case ConnectionResult.SIGN_IN_REQUIRED:
+                return "SIGN_IN_REQUIRED(" + errorCode + ")";
+            case ConnectionResult.SUCCESS:
+                return "SUCCESS(" + errorCode + ")";
+            default:
+                return "Unknown error code " + errorCode;
+        }
+    }
+
+    // Represents the reason for a sign-in failure
+    public static class SignInFailureReason {
+        public static final int NO_ACTIVITY_RESULT_CODE = -100;
+        int mServiceErrorCode = 0;
+        int mActivityResultCode = NO_ACTIVITY_RESULT_CODE;
+        public int getServiceErrorCode() {
+            return mServiceErrorCode;
+        }
+        public int getActivityResultCode() {
+            return mActivityResultCode;
+        }
+        public SignInFailureReason(int serviceErrorCode, int activityResultCode) {
+            mServiceErrorCode = serviceErrorCode;
+            mActivityResultCode = activityResultCode;
+        }
+        public SignInFailureReason(int serviceErrorCode) {
+            this(serviceErrorCode, NO_ACTIVITY_RESULT_CODE);
+        }
+        @Override
+        public String toString() {
+            return "SignInFailureReason(serviceErrorCode:" +
+                    errorCodeToString(mServiceErrorCode) +
+                    ((mActivityResultCode == NO_ACTIVITY_RESULT_CODE) ? ")" :
+                    (",activityResultCode:" +
+                    activityResponseCodeToString(mActivityResultCode) + ")"));
+        }
+    }
+
+    void printMisconfiguredDebugInfo() {
+        debugLog("****");
+        debugLog("****");
+        debugLog("**** APP NOT CORRECTLY CONFIGURED TO USE GOOGLE PLAY GAME SERVICES");
+        debugLog("**** This is usually caused by one of these reasons:");
+        debugLog("**** (1) Your package name and certificate fingerprint do not match");
+        debugLog("****     the client ID you registered in Developer Console.");
+        debugLog("**** (2) Your App ID was incorrectly entered.");
+        debugLog("**** (3) Your game settings have not been published and you are ");
+        debugLog("****     trying to log in with an account that is not listed as");
+        debugLog("****     a test account.");
+        debugLog("****");
+        Context ctx = getContext();
+        if (ctx == null) {
+            debugLog("*** (no Context, so can't print more debug info)");
+            return;
+        }
+
+        debugLog("**** To help you debug, here is the information about this app");
+        debugLog("**** Package name         : " + getContext().getPackageName());
+        debugLog("**** Cert SHA1 fingerprint: " + getSHA1CertFingerprint());
+        debugLog("**** App ID from          : " + getAppIdFromResource());
+        debugLog("****");
+        debugLog("**** Check that the above information matches your setup in ");
+        debugLog("**** Developer Console. Also, check that you're logging in with the");
+        debugLog("**** right account (it should be listed in the Testers section if");
+        debugLog("**** your project is not yet published).");
+        debugLog("****");
+        debugLog("**** For more information, refer to the troubleshooting guide:");
+        debugLog("****   http://developers.google.com/games/services/android/troubleshooting");
+    }
+
+    String getAppIdFromResource() {
+        try {
+            Resources res = getContext().getResources();
+            String pkgName = getContext().getPackageName();
+            int res_id = res.getIdentifier("app_id", "string", pkgName);
+            return res.getString(res_id);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return "??? (failed to retrieve APP ID)";
+        }
+    }
+
+    String getSHA1CertFingerprint() {
+        try {
+            Signature[] sigs = getContext().getPackageManager().getPackageInfo(
+                    getContext().getPackageName(), PackageManager.GET_SIGNATURES).signatures;
+            if (sigs.length == 0) {
+                return "ERROR: NO SIGNATURE.";
+            } else if (sigs.length > 1) {
+                return "ERROR: MULTIPLE SIGNATURES";
+            }
+            byte[] digest = MessageDigest.getInstance("SHA1").digest(sigs[0].toByteArray());
+            StringBuilder hexString = new StringBuilder();
+            for (int i = 0; i < digest.length; ++i) {
+                if (i > 0) {
+                    hexString.append(":");
+                }
+                byteToString(hexString, digest[i]);
+            }
+            return hexString.toString();
+
+        } catch (PackageManager.NameNotFoundException ex) {
+            ex.printStackTrace();
+            return "(ERROR: package not found)";
+        } catch (NoSuchAlgorithmException ex) {
+            ex.printStackTrace();
+            return "(ERROR: SHA1 algorithm not found)";
+        }
+    }
+
+    void byteToString(StringBuilder sb, byte b) {
+        int unsigned_byte = b < 0 ? b + 256 : b;
+        int hi = unsigned_byte / 16;
+        int lo = unsigned_byte % 16;
+        sb.append("0123456789ABCDEF".substring(hi, hi + 1));
+        sb.append("0123456789ABCDEF".substring(lo, lo + 1));
+    }
+
+}

+ 117 - 0
gameplay/android/java/src/org/gameplay3d/GooglePlaySocial.java

@@ -0,0 +1,117 @@
+    
+package org.gameplay3d;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.widget.Toast;
+import com.google.android.gms.games.Player;
+import com.google.example.games.basegameutils.BaseGameActivity;
+
+public class GooglePlaySocial extends BaseGameActivity 
+{	
+	private static List<Player> mFriends = new ArrayList<Player>();
+    private static final int FRIENDS_PER_PAGE = 10;
+	
+	public static void gameServicesSignIn() 
+	{
+        ((appname)mContext).runOnUiThread(new Runnable() 
+		{
+            public void run() {
+                ((appname)mContext).beginUserInitiatedSignIn();
+            }
+        });
+    }
+
+    public static void updateTopScoreLeaderboard(int score) 
+	{
+		if (mContext.isSignedIn())
+			mContext.getGamesClient().submitScore("leaderboardid", score);
+    }
+
+	public static void submitAchievement(String achievementId)
+	{
+        if (mContext.isSignedIn())
+		{
+            mContext.getGamesClient().unlockAchievement(achievementId);
+        }
+    }
+	
+	public static void incrementAchievement(String achievementId, int percentage)
+	{
+        if (mContext.isSignedIn())
+		{
+            mContext.getGamesClient().incrementAchievement(achievementId, percentage);
+        }
+    }
+
+    public static void displayLeaderboards() 
+	{
+		if (mContext.isSignedIn())
+		{
+			((appname)mContext).runOnUiThread(new Runnable() 
+			{
+				public void run() {
+					((appname)mContext).startActivityForResult(((appname)mContext).getGamesClient().getLeaderboardIntent("leaderboardidfromgoogleplay"), 5001);
+				}
+			});
+		}
+		else
+		{
+			gameServicesSignIn();
+		}
+    }
+
+    public static void displayAchievements() 
+	{
+		if (mContext.isSignedIn())
+		{
+			((appname)mContext).runOnUiThread(new Runnable() 
+			{
+				public void run() {
+					((appname)mContext).startActivityForResult(((appname)mContext).getGamesClient().getAchievementsIntent(), 5001);
+				}
+			});
+		}
+		else
+		{
+			gameServicesSignIn();
+		}
+    }
+
+    public static void loadFriends() 
+	{
+        if (mFriends.size() > 0) {
+            mFriends.clear();
+        }
+
+        ((appname)mContext).runOnUiThread(new Runnable() 
+		{
+                public void run() 
+				{        
+            ((appname)mContext).getGamesClient().loadInvitablePlayers(new OnPlayersLoadedListener() {
+
+                @Override
+                public void onPlayersLoaded(int statusCode, PlayerBuffer playerBuffer) {
+
+                    if (statusCode == GamesClient.STATUS_OK) {
+                        for (Player player : playerBuffer) {
+                            mFriends.add(player);
+                        }
+
+                        if (playerBuffer.getCount() == FRIENDS_PER_PAGE) {
+                            ((appname)mContext).getGamesClient().loadMoreInvitablePlayers(this, FRIENDS_PER_PAGE);
+                        } else {
+                            // call out and return all the friends <more code to call into C++>
+
+                            for (Player friend : mFriends) {
+                                Log.i(TAG, String.format("Found player with id [%s] and display name [%s]", friend.getPlayerId(), friend.getDisplayName()));
+                            }
+                        }
+                    }
+                }
+            }, FRIENDS_PER_PAGE, false);
+        });
+    }
+}

+ 11 - 4
gameplay/android/jni/Android.mk

@@ -27,6 +27,7 @@ LOCAL_SRC_FILES := \
     CheckBox.cpp \
     Container.cpp \
     Control.cpp \
+    ControlFactory.cpp \
     Curve.cpp \
     DebugNew.cpp \
     DepthStencilTarget.cpp \
@@ -41,7 +42,7 @@ LOCAL_SRC_FILES := \
     Gamepad.cpp \
     HeightField.cpp \
     Image.cpp \
-	ImageControl.cpp \
+    ImageControl.cpp \
     Joint.cpp \
     Joystick.cpp \
     Label.cpp \
@@ -133,12 +134,14 @@ LOCAL_SRC_FILES := \
     lua/lua_Bundle.cpp \
     lua/lua_Button.cpp \
     lua/lua_Camera.cpp \
+    lua/lua_CameraListener.cpp \
     lua/lua_CameraType.cpp \
     lua/lua_CheckBox.cpp \
     lua/lua_Container.cpp \
     lua/lua_ContainerScroll.cpp \
     lua/lua_Control.cpp \
     lua/lua_ControlAlignment.cpp \
+    lua/lua_ControlAutoSize.cpp \
     lua/lua_ControlListener.cpp \
     lua/lua_ControlListenerEventType.cpp \
     lua/lua_ControlState.cpp \
@@ -150,6 +153,7 @@ LOCAL_SRC_FILES := \
     lua/lua_FileSystem.cpp \
     lua/lua_FlowLayout.cpp \
     lua/lua_Font.cpp \
+    lua/lua_FontFormat.cpp \
     lua/lua_FontJustify.cpp \
     lua/lua_FontStyle.cpp \
     lua/lua_FontText.cpp \
@@ -239,10 +243,12 @@ LOCAL_SRC_FILES := \
     lua/lua_RenderStateBlend.cpp \
     lua/lua_RenderStateCullFaceSide.cpp \
     lua/lua_RenderStateDepthFunction.cpp \
+    lua/lua_RenderStateFrontFace.cpp  \
     lua/lua_RenderStateStateBlock.cpp \
+    lua/lua_RenderStateStencilFunction.cpp \
+    lua/lua_RenderStateStencilOperation.cpp \
     lua/lua_RenderTarget.cpp \
     lua/lua_Scene.cpp \
-    lua/lua_SceneDebugFlags.cpp \
     lua/lua_ScreenDisplayer.cpp \
     lua/lua_ScriptController.cpp \
     lua/lua_ScriptTarget.cpp \
@@ -251,8 +257,9 @@ LOCAL_SRC_FILES := \
     lua/lua_Technique.cpp \
     lua/lua_Terrain.cpp \
     lua/lua_TerrainFlags.cpp \
-    lua/lua_TerrainListener.cpp \
+    lua/lua_TerrainPatch.cpp \
     lua/lua_TextBox.cpp \
+    lua/lua_TextBoxInputMode.cpp \
     lua/lua_Texture.cpp \
     lua/lua_TextureFilter.cpp \
     lua/lua_TextureFormat.cpp \
@@ -278,7 +285,7 @@ LOCAL_SRC_FILES := \
     lua/lua_VerticalLayout.cpp
 
     
-LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/lua/include" -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include" -I"../../external-deps/oggvorbis/include" -I"../../external-deps/openal/include"
+LOCAL_CFLAGS := -D__ANDROID__ -DGP_USE_SOCIAL -I"../../external-deps/lua/include" -I"../../external-deps/bullet/include" -I"../../external-deps/png/include" -I"../../external-deps/oggvorbis/include" -I"../../external-deps/openal/include"
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 
 include $(BUILD_STATIC_LIBRARY)

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

@@ -1,2 +1,3 @@
 APP_STL     := stlport_static
-APP_MODULES := libgameplay
+APP_MODULES := libgameplay
+APP_ABI     := armeabi-v7a

+ 43 - 260
gameplay/gameplay.vcxproj

@@ -1,14 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="DebugMem|BlackBerry">
-      <Configuration>DebugMem</Configuration>
-      <Platform>BlackBerry</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="DebugMem|BlackBerrySimulator">
-      <Configuration>DebugMem</Configuration>
-      <Platform>BlackBerrySimulator</Platform>
-    </ProjectConfiguration>
     <ProjectConfiguration Include="DebugMem|Win32">
       <Configuration>DebugMem</Configuration>
       <Platform>Win32</Platform>
@@ -17,14 +9,6 @@
       <Configuration>DebugMem</Configuration>
       <Platform>x64</Platform>
     </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|BlackBerry">
-      <Configuration>Debug</Configuration>
-      <Platform>BlackBerry</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|BlackBerrySimulator">
-      <Configuration>Debug</Configuration>
-      <Platform>BlackBerrySimulator</Platform>
-    </ProjectConfiguration>
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
       <Platform>Win32</Platform>
@@ -33,14 +17,6 @@
       <Configuration>Debug</Configuration>
       <Platform>x64</Platform>
     </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|BlackBerry">
-      <Configuration>Release</Configuration>
-      <Platform>BlackBerry</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|BlackBerrySimulator">
-      <Configuration>Release</Configuration>
-      <Platform>BlackBerrySimulator</Platform>
-    </ProjectConfiguration>
     <ProjectConfiguration Include="Release|Win32">
       <Configuration>Release</Configuration>
       <Platform>Win32</Platform>
@@ -73,6 +49,7 @@
     <ClCompile Include="src\CheckBox.cpp" />
     <ClCompile Include="src\Container.cpp" />
     <ClCompile Include="src\Control.cpp" />
+    <ClCompile Include="src\ControlFactory.cpp" />
     <ClCompile Include="src\Curve.cpp" />
     <ClCompile Include="src\DebugNew.cpp" />
     <ClCompile Include="src\DepthStencilTarget.cpp" />
@@ -125,12 +102,14 @@
     <ClCompile Include="src\lua\lua_Bundle.cpp" />
     <ClCompile Include="src\lua\lua_Button.cpp" />
     <ClCompile Include="src\lua\lua_Camera.cpp" />
+    <ClCompile Include="src\lua\lua_CameraListener.cpp" />
     <ClCompile Include="src\lua\lua_CameraType.cpp" />
     <ClCompile Include="src\lua\lua_CheckBox.cpp" />
     <ClCompile Include="src\lua\lua_Container.cpp" />
     <ClCompile Include="src\lua\lua_ContainerScroll.cpp" />
     <ClCompile Include="src\lua\lua_Control.cpp" />
     <ClCompile Include="src\lua\lua_ControlAlignment.cpp" />
+    <ClCompile Include="src\lua\lua_ControlAutoSize.cpp" />
     <ClCompile Include="src\lua\lua_ControlListener.cpp" />
     <ClCompile Include="src\lua\lua_ControlListenerEventType.cpp" />
     <ClCompile Include="src\lua\lua_ControlState.cpp" />
@@ -142,6 +121,7 @@
     <ClCompile Include="src\lua\lua_FileSystem.cpp" />
     <ClCompile Include="src\lua\lua_FlowLayout.cpp" />
     <ClCompile Include="src\lua\lua_Font.cpp" />
+    <ClCompile Include="src\lua\lua_FontFormat.cpp" />
     <ClCompile Include="src\lua\lua_FontJustify.cpp" />
     <ClCompile Include="src\lua\lua_FontStyle.cpp" />
     <ClCompile Include="src\lua\lua_FontText.cpp" />
@@ -231,10 +211,12 @@
     <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_RenderStateFrontFace.cpp" />
     <ClCompile Include="src\lua\lua_RenderStateStateBlock.cpp" />
+    <ClCompile Include="src\lua\lua_RenderStateStencilFunction.cpp" />
+    <ClCompile Include="src\lua\lua_RenderStateStencilOperation.cpp" />
     <ClCompile Include="src\lua\lua_RenderTarget.cpp" />
     <ClCompile Include="src\lua\lua_Scene.cpp" />
-    <ClCompile Include="src\lua\lua_SceneDebugFlags.cpp" />
     <ClCompile Include="src\lua\lua_ScreenDisplayer.cpp" />
     <ClCompile Include="src\lua\lua_ScriptController.cpp" />
     <ClCompile Include="src\lua\lua_ScriptTarget.cpp" />
@@ -243,8 +225,9 @@
     <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_TerrainPatch.cpp" />
     <ClCompile Include="src\lua\lua_TextBox.cpp" />
+    <ClCompile Include="src\lua\lua_TextBoxInputMode.cpp" />
     <ClCompile Include="src\lua\lua_Texture.cpp" />
     <ClCompile Include="src\lua\lua_TextureFilter.cpp" />
     <ClCompile Include="src\lua\lua_TextureFormat.cpp" />
@@ -355,6 +338,7 @@
     <ClInclude Include="src\CheckBox.h" />
     <ClInclude Include="src\Container.h" />
     <ClInclude Include="src\Control.h" />
+    <ClInclude Include="src\ControlFactory.h" />
     <ClInclude Include="src\Curve.h" />
     <ClInclude Include="src\DebugNew.h" />
     <ClInclude Include="src\DepthStencilTarget.h" />
@@ -406,12 +390,14 @@
     <ClInclude Include="src\lua\lua_Bundle.h" />
     <ClInclude Include="src\lua\lua_Button.h" />
     <ClInclude Include="src\lua\lua_Camera.h" />
+    <ClInclude Include="src\lua\lua_CameraListener.h" />
     <ClInclude Include="src\lua\lua_CameraType.h" />
     <ClInclude Include="src\lua\lua_CheckBox.h" />
     <ClInclude Include="src\lua\lua_Container.h" />
     <ClInclude Include="src\lua\lua_ContainerScroll.h" />
     <ClInclude Include="src\lua\lua_Control.h" />
     <ClInclude Include="src\lua\lua_ControlAlignment.h" />
+    <ClInclude Include="src\lua\lua_ControlAutoSize.h" />
     <ClInclude Include="src\lua\lua_ControlListener.h" />
     <ClInclude Include="src\lua\lua_ControlListenerEventType.h" />
     <ClInclude Include="src\lua\lua_ControlState.h" />
@@ -423,6 +409,7 @@
     <ClInclude Include="src\lua\lua_FileSystem.h" />
     <ClInclude Include="src\lua\lua_FlowLayout.h" />
     <ClInclude Include="src\lua\lua_Font.h" />
+    <ClInclude Include="src\lua\lua_FontFormat.h" />
     <ClInclude Include="src\lua\lua_FontJustify.h" />
     <ClInclude Include="src\lua\lua_FontStyle.h" />
     <ClInclude Include="src\lua\lua_FontText.h" />
@@ -512,10 +499,12 @@
     <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_RenderStateFrontFace.h" />
     <ClInclude Include="src\lua\lua_RenderStateStateBlock.h" />
+    <ClInclude Include="src\lua\lua_RenderStateStencilFunction.h" />
+    <ClInclude Include="src\lua\lua_RenderStateStencilOperation.h" />
     <ClInclude Include="src\lua\lua_RenderTarget.h" />
     <ClInclude Include="src\lua\lua_Scene.h" />
-    <ClInclude Include="src\lua\lua_SceneDebugFlags.h" />
     <ClInclude Include="src\lua\lua_ScreenDisplayer.h" />
     <ClInclude Include="src\lua\lua_ScriptController.h" />
     <ClInclude Include="src\lua\lua_ScriptTarget.h" />
@@ -524,8 +513,9 @@
     <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_TerrainPatch.h" />
     <ClInclude Include="src\lua\lua_TextBox.h" />
+    <ClInclude Include="src\lua\lua_TextBoxInputMode.h" />
     <ClInclude Include="src\lua\lua_Texture.h" />
     <ClInclude Include="src\lua\lua_TextureFilter.h" />
     <ClInclude Include="src\lua\lua_TextureFormat.h" />
@@ -613,35 +603,21 @@
     <ClInclude Include="src\VerticalLayout.h" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="res\logo_black.png" />
-    <None Include="res\logo_powered_black.png" />
-    <None Include="res\logo_powered_white.png" />
-    <None Include="res\logo_white.png" />
-    <None Include="res\shaders\colored-unlit.frag" />
-    <None Include="res\shaders\colored-unlit.vert" />
+    <None Include="res\materials\terrain.material" />
     <None Include="res\shaders\colored.frag" />
     <None Include="res\shaders\colored.vert" />
     <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\lighting.vert" />
     <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" />
-    <None Include="res\shaders\textured-bumped.vert" />
-    <None Include="res\shaders\textured-unlit.frag" />
-    <None Include="res\shaders\textured-unlit.vert" />
     <None Include="res\shaders\textured.frag" />
     <None Include="res\shaders\textured.vert" />
     <None Include="src\BoundingBox.inl" />
@@ -678,65 +654,39 @@
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerry'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerrySimulator'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerry'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerrySimulator'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerry'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerrySimulator'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
+    <PlatformToolset>v120</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -747,36 +697,18 @@
   <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerry'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerrySimulator'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
   <ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'" Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
   <ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'" Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerry'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerrySimulator'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
   <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
   <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
     <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
   </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerry'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
-  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerrySimulator'" Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-  </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <OutDir>windows\x86\$(Configuration)\</OutDir>
@@ -784,78 +716,36 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <OutDir>windows\x64\$(Configuration)\</OutDir>
   </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerry'">
-    <OutDir>Device-$(Configuration)\</OutDir>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerrySimulator'">
-    <OutDir>Simulator\</OutDir>
-  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'">
     <OutDir>windows\x86\$(Configuration)\</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'">
     <OutDir>windows\x64\$(Configuration)\</OutDir>
   </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerry'">
-    <OutDir>Device-$(Configuration)\</OutDir>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerrySimulator'">
-    <OutDir>Simulator\</OutDir>
-  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <IntDir>windows\x86\$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <IntDir>windows\x64\$(Configuration)\</IntDir>
   </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerry'">
-    <IntDir>Device-$(Configuration)\</IntDir>
-    <TargetName>lib$(ProjectName)</TargetName>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerrySimulator'">
-    <IntDir>Simulator\</IntDir>
-    <TargetName>lib$(ProjectName)</TargetName>
-  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'">
     <IntDir>windows\x86\$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'">
     <IntDir>windows\x64\$(Configuration)\</IntDir>
   </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerry'">
-    <IntDir>Device-$(Configuration)\</IntDir>
-    <TargetName>lib$(ProjectName)</TargetName>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerrySimulator'">
-    <IntDir>Simulator\</IntDir>
-    <TargetName>lib$(ProjectName)</TargetName>
-  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <OutDir>windows\x86\$(Configuration)\</OutDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <OutDir>windows\x64\$(Configuration)\</OutDir>
   </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerry'">
-    <OutDir>Device-$(Configuration)\</OutDir>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerrySimulator'">
-    <OutDir>Simulator\</OutDir>
-  </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <IntDir>windows\x86\$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <IntDir>windows\x64\$(Configuration)\</IntDir>
   </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerry'">
-    <IntDir>Device-$(Configuration)\</IntDir>
-    <TargetName>lib$(ProjectName)</TargetName>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerrySimulator'">
-    <IntDir>Simulator\</IntDir>
-    <TargetName>lib$(ProjectName)</TargetName>
-  </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
       <PrecompiledHeader>
@@ -863,10 +753,12 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>_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>
+      <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\png\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>
       </RuntimeTypeInfo>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <DisableSpecificWarnings>
+      </DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -883,7 +775,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>_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>
+      <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\png\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>
       </RuntimeTypeInfo>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@@ -896,51 +788,18 @@
       <TargetMachine>MachineX64</TargetMachine>
     </Lib>
   </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerry'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <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>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
-      <AdditionalOptions>-mfpu=neon %(AdditionalOptions)</AdditionalOptions>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|BlackBerrySimulator'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>_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>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|Win32'">
     <ClCompile>
       <PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;GAMEPLAY_MEM_LEAK_DETECTION;%(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>
+      <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;GP_USE_MEM_LEAK_DETECTION;%(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\png\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>true</RuntimeTypeInfo>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <DisableSpecificWarnings>
+      </DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -961,49 +820,8 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;GAMEPLAY_MEM_LEAK_DETECTION;%(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>true</RuntimeTypeInfo>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-    <Lib>
-      <Verbose>
-      </Verbose>
-    </Lib>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerry'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>__BB10__;_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;GAMEPLAY_MEM_LEAK_DETECTION;%(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>true</RuntimeTypeInfo>
-      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
-      <AdditionalOptions>-mfpu=neon %(AdditionalOptions)</AdditionalOptions>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-    </Link>
-    <Lib>
-      <Verbose>
-      </Verbose>
-    </Lib>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|BlackBerrySimulator'">
-    <ClCompile>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;GAMEPLAY_MEM_LEAK_DETECTION;%(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>
+      <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;GP_USE_MEM_LEAK_DETECTION;%(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\png\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>true</RuntimeTypeInfo>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
     </ClCompile>
@@ -1025,9 +843,11 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_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>
+      <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\png\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <DebugInformationFormat>
       </DebugInformationFormat>
+      <DisableSpecificWarnings>
+      </DisableSpecificWarnings>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -1045,7 +865,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_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>
+      <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\png\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <DebugInformationFormat>
       </DebugInformationFormat>
     </ClCompile>
@@ -1056,44 +876,7 @@
       <OptimizeReferences>true</OptimizeReferences>
     </Link>
   </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerry'">
-    <ClCompile>
-      <WarningLevel>Level3</WarningLevel>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_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>
-      <AdditionalOptions>-mfpu=neon %(AdditionalOptions)</AdditionalOptions>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|BlackBerrySimulator'">
-    <ClCompile>
-      <WarningLevel>Level3</WarningLevel>
-      <PrecompiledHeader>
-      </PrecompiledHeader>
-      <Optimization>MaxSpeed</Optimization>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_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>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation>true</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 616 - 568
gameplay/gameplay.vcxproj.filters


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1190 - 1741
gameplay/gameplay.xcodeproj/project.pbxproj


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

@@ -1,7 +0,0 @@
-<?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 = "0460"
+   LastUpgradeVersion = "0500"
    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 = "0460"
+   LastUpgradeVersion = "0500"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 0 - 0
samples/lua/res/arial.ttf → gameplay/res/design/arial.ttf


+ 0 - 0
gameplay/res/icon_128.ico → gameplay/res/design/icon_128.ico


+ 0 - 0
gameplay/res/icon_16.ico → gameplay/res/design/icon_16.ico


+ 0 - 0
gameplay/res/icon_32.ico → gameplay/res/design/icon_32.ico


+ 0 - 0
gameplay/res/icon_64.ico → gameplay/res/design/icon_64.ico


+ 0 - 0
gameplay/res/logo.ai → gameplay/res/design/logo.ai


+ 41 - 0
gameplay/res/materials/terrain.material

@@ -0,0 +1,41 @@
+//
+// Default Terrain material file.
+// 
+// In addition to the built-in Node material auto-bindings, the following terrain-specific
+// auto-bindings are also supported:
+//
+// TERRAIN_NORMAL_MAP                   : normal map sampler (if using a normal map)
+// TERRAIN_LAYER_MAPS                   : array of texture samplers for each terrain layer
+// TERRAIN_ROW                          : row index of the current terrain patch
+// TERRAIN_COLUMN                       : column index of the current terrain patch
+//
+// To add lighting (other than ambient) to a terrain, you can add additional pass defines and
+// uniform bindings and handle them in your specific game or renderer. See the gameplay
+// terrain sample for an example.
+//
+material terrain
+{
+    u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX
+
+    u_normalMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX
+    //u_normalMap = TERRAIN_NORMAL_MAP
+
+    u_surfaceLayerMaps = TERRAIN_LAYER_MAPS
+
+    u_ambientColor = SCENE_AMBIENT_COLOR
+
+    renderState
+    {
+        cullFace = true
+        depthTest = true
+    }
+
+    technique
+    {
+        pass
+        {
+            vertexShader = res/shaders/terrain.vert
+            fragmentShader = res/shaders/terrain.frag
+        }
+    }
+}

+ 0 - 45
gameplay/res/shaders/colored-unlit.frag

@@ -1,45 +0,0 @@
-#ifdef OPENGL_ES
-precision highp float;
-#endif
-
-// Uniforms
-uniform vec4 u_diffuseColor;               	// Diffuse color
-#if defined(TEXTURE_LIGHTMAP)
-uniform sampler2D u_lightmapTexture;     	// Lightmap texture
-#endif
-#if defined(MODULATE_COLOR)
-uniform vec4 u_modulateColor;               // Modulation color
-#endif
-#if defined(MODULATE_ALPHA)
-uniform float u_modulateAlpha;              // Modulation alpha
-#endif
-
-// Varyings
-#if defined(VERTEX_COLOR)
-varying vec3 v_color;						// Input Vertex color ( r g b )
-#endif
-#if defined(TEXTURE_LIGHTMAP)
-varying vec2 v_texCoord;
-#endif
-
-
-void main()
-{
-    // Set base diffuse color
-    #if defined(VERTEX_COLOR)
-	gl_FragColor.rgb = v_color;
-	#else
-	gl_FragColor = u_diffuseColor;
-    #endif
-	#if defined(TEXTURE_LIGHTMAP)
-	vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord);
-	gl_FragColor.rgb *= lightColor.rgb;
-	#endif
-	// Global color modulation
-	#if defined(MODULATE_COLOR)
-	gl_FragColor *= u_modulateColor;
-	#endif
-	#if defined(MODULATE_ALPHA)
-    gl_FragColor.a *= u_modulateAlpha;
-    #endif
-}

+ 0 - 53
gameplay/res/shaders/colored-unlit.vert

@@ -1,53 +0,0 @@
-// Attributes
-attribute vec4 a_position;									// Vertex Position							(x, y, z, w)
-#if defined(TEXTURE_LIGHTMAP)
-attribute vec2 a_texCoord;                                  // Texture Coordinate (for lightmapping)
-#endif
-#if defined(SKINNING)
-attribute vec4 a_blendWeights;								// Vertex blend weight, up to 4				(0, 1, 2, 3) 
-attribute vec4 a_blendIndices;								// Vertex blend index int u_matrixPalette	(0, 1, 2, 3)
-#endif
-#if defined(VERTEX_COLOR)
-attribute vec3 a_color;										// Vertex Color								(r, g, b)
-#endif
-
-// Uniforms
-uniform mat4 u_worldViewProjectionMatrix;					// Matrix to transform a position to clip space.
-#if defined(SKINNING)
-uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices as an array of floats
-#endif
-
-// Varyings
-#if defined(TEXTURE_LIGHTMAP)
-varying vec2 v_texCoord;                                    // Output Texture Coordinate
-#endif
-#if defined(VERTEX_COLOR)
-varying vec3 v_color;										// Output Vertex color						(r, g, b)
-#endif
-
-// Skinning
-#if defined(SKINNING)
-#include "skinning.vert"
-#else
-#include "skinning-none.vert" 
-#endif
-
-
-void main()
-{
-    // Get the vertex position
-    vec4 position = getPosition();
-    
-    // Transform position to clip space.a
-    gl_Position = u_worldViewProjectionMatrix *  position;
-    
-    // Pass lightmap tex coord to fragment shader
-    #if defined(TEXTURE_LIGHTMAP)
-    v_texCoord0 = a_texCoord0;
-    #endif
-
-     // Pass on vertex color to fragment shader
-    #if defined(VERTEX_COLOR)
-	v_color = a_color;
-    #endif
-}

+ 134 - 74
gameplay/res/shaders/colored.frag

@@ -1,74 +1,134 @@
-#define LIGHTING
-#ifdef OPENGL_ES
-precision highp float;
-#endif
-
-// Uniforms
-uniform vec4 u_diffuseColor;               		// Diffuse color
-uniform vec3 u_ambientColor;                    // Ambient color
-uniform vec3 u_lightColor;                      // Light color
-uniform vec3 u_lightDirection;					// Light direction
-#if defined(SPECULAR)
-uniform float u_specularExponent;				// Specular exponent
-#endif
-#if defined(SPOT_LIGHT)
-uniform float u_spotLightInnerAngleCos;			// The bright spot [0.0 - 1.0]
-uniform float u_spotLightOuterAngleCos;			// The soft outer part [0.0 - 1.0]
-uniform vec3 u_spotLightDirection;              // Direction of a spot light source
-#endif
-#if defined(MODULATE_COLOR)
-uniform vec4 u_modulateColor;					// Modulation color
-#endif
-#if defined(MODULATE_ALPHA)
-uniform float u_modulateAlpha;					// Modulation alpha
-#endif
-
-// Inputs
-varying vec3 v_normalVector;					// Normal vector in view space
-#if defined(VERTEX_COLOR)
-varying vec3 v_color;							// Vertex color
-#endif
-#if defined(POINT_LIGHT)
-varying vec3 v_vertexToPointLightDirection;		// Light direction w.r.t current vertex in tangent space
-varying float v_pointLightAttenuation;			// Attenuation of point light
-#elif defined(SPOT_LIGHT)
-varying vec3 v_spotLightDirection;				// Direction of spot light in tangent space.
-varying vec3 v_vertexToSpotLightDirection;		// Direction of the spot light w.r.t current vertex in tangent space
-varying float v_spotLightAttenuation;			// Attenuation of spot light
-#else
-varying vec3 v_lightDirection;					// Direction of light in tangent space
-#endif
-#if defined(SPECULAR)
-varying vec3 v_cameraDirection;                 // Camera direction
-#endif
-
-// Lighting
-#include "lighting.frag"
-#if defined(POINT_LIGHT)
-#include "lighting-point.frag"
-#elif defined(SPOT_LIGHT)
-#include "lighting-spot.frag"
-#else
-#include "lighting-directional.frag"
-#endif
-
-void main()
-{
-    // Set base diffuse color
-    #if defined(VERTEX_COLOR)
-	_baseColor.rgb = v_color;
-	#else
-	_baseColor = u_diffuseColor;
-	#endif
-
-    // Light the pixel
-    gl_FragColor.a = _baseColor.a;
-    gl_FragColor.rgb = getLitPixel();
-    
-	#if defined(MODULATE_COLOR)
-    gl_FragColor *= u_modulateColor;
-    #endif
-	#if defined(MODULATE_ALPHA)
-    gl_FragColor.a *= u_modulateAlpha;
-    #endif
-}
+#ifdef OPENGL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+#endif
+
+#ifndef DIRECTIONAL_LIGHT_COUNT
+#define DIRECTIONAL_LIGHT_COUNT 0
+#endif
+#ifndef SPOT_LIGHT_COUNT
+#define SPOT_LIGHT_COUNT 0
+#endif
+#ifndef POINT_LIGHT_COUNT
+#define POINT_LIGHT_COUNT 0
+#endif
+#if (DIRECTIONAL_LIGHT_COUNT > 0) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+#define LIGHTING
+#endif
+
+///////////////////////////////////////////////////////////
+// Uniforms
+uniform vec3 u_ambientColor;
+uniform vec4 u_diffuseColor;
+
+#if defined(LIGHTMAP)
+uniform sampler2D u_lightmapTexture;
+#endif
+
+#if defined(LIGHTING)
+
+#if (DIRECTIONAL_LIGHT_COUNT > 0)
+uniform vec3 u_directionalLightColor[DIRECTIONAL_LIGHT_COUNT];
+uniform vec3 u_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+
+#if (POINT_LIGHT_COUNT > 0)
+uniform vec3 u_pointLightColor[POINT_LIGHT_COUNT];
+uniform vec3 u_pointLightPosition[POINT_LIGHT_COUNT];
+uniform float u_pointLightRangeInverse[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+uniform vec3 u_spotLightColor[SPOT_LIGHT_COUNT];
+uniform vec3 u_spotLightDirection[SPOT_LIGHT_COUNT];
+uniform float u_spotLightRangeInverse[SPOT_LIGHT_COUNT];
+uniform float u_spotLightInnerAngleCos[SPOT_LIGHT_COUNT];
+uniform float u_spotLightOuterAngleCos[SPOT_LIGHT_COUNT];
+#endif
+
+#if defined(SPECULAR)
+uniform float u_specularExponent;
+#endif
+
+#endif
+
+#if defined(MODULATE_COLOR)
+uniform vec4 u_modulateColor;
+#endif
+
+#if defined(MODULATE_ALPHA)
+uniform float u_modulateAlpha;
+#endif
+
+///////////////////////////////////////////////////////////
+// Variables
+vec4 _baseColor;
+
+///////////////////////////////////////////////////////////
+// Varyings
+#if defined(VERTEX_COLOR)
+varying vec3 v_color;
+#endif
+
+#if defined(LIGHTING)
+
+varying vec3 v_normalVector;
+
+#if (POINT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToPointLightDirection[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToSpotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+
+#if defined(SPECULAR)
+varying vec3 v_cameraDirection; 
+#endif
+
+#include "lighting.frag"
+
+#endif
+
+
+void main()
+{
+    
+    #if defined(LIGHTING)
+
+    #if defined(VERTEX_COLOR)
+	_baseColor.rgb = v_color;
+    #else
+    _baseColor = u_diffuseColor;
+	#endif
+    
+    gl_FragColor.a = _baseColor.a;
+    gl_FragColor.rgb = getLitPixel();
+    
+    #else
+    
+    #if defined(VERTEX_COLOR)
+    gl_FragColor.rgb = v_color;
+    gl_FragColor.a = 1.0;
+    #else
+    gl_FragColor = u_diffuseColor;
+    #endif
+    
+    #endif
+
+	#if defined(LIGHTMAP)
+	vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord1);
+	gl_FragColor.rgb *= lightColor.rgb;
+	#endif
+
+	#if defined(MODULATE_COLOR)
+    gl_FragColor *= u_modulateColor;
+    #endif
+
+	#if defined(MODULATE_ALPHA)
+    gl_FragColor.a *= u_modulateAlpha;
+    #endif
+}

+ 137 - 82
gameplay/res/shaders/colored.vert

@@ -1,82 +1,137 @@
-#define LIGHTING
-
-// Attributes
-attribute vec4 a_position;									// Vertex Position							(x, y, z, w)
-attribute vec3 a_normal;									// Vertex Normal							(x, y, z)
-#if defined(SKINNING)
-attribute vec4 a_blendWeights;								// Vertex blend weight, up to 4				(0, 1, 2, 3) 
-attribute vec4 a_blendIndices;								// Vertex blend index int u_matrixPalette	(0, 1, 2, 3)
-#endif
-#if defined(VERTEX_COLOR)
-attribute vec3 a_color;										// Output Vertex Color
-varying vec3 v_color;										// Output Vertex Color 
-#endif
-
-// 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 vec3 u_cameraPosition;                 				// Position of the camera in view space.
-#endif
-#if defined(POINT_LIGHT)
-uniform vec3 u_pointLightPosition;							// Position of light
-uniform float u_pointLightRangeInverse;						// Inverse of light range 
-#elif defined(SPOT_LIGHT)
-uniform vec3 u_spotLightPosition;							// Position of light
-uniform float u_spotLightRangeInverse;						// Inverse of light range.
-#else
-#endif
-
-// Varyings
-varying vec3 v_normalVector;								// Normal vector in view space.
-#if defined(SPECULAR)
-varying vec3 v_cameraDirection;								// Direction the camera is looking at in tangent space.
-#endif
-
-// Lighting
-#if defined(POINT_LIGHT)
-varying vec3 v_vertexToPointLightDirection;					// Direction of point light w.r.t current vertex in tangent space.
-varying float v_pointLightAttenuation;						// Attenuation of point light.
-#include "lighting-point.vert"
-#elif defined(SPOT_LIGHT)
-varying vec3 v_vertexToSpotLightDirection;					// Direction of the spot light w.r.t current vertex in tangent space.
-varying float v_spotLightAttenuation;						// Attenuation of spot light.
-#include "lighting-spot.vert"
-#else
-uniform vec3 u_lightDirection;								// Direction of light
-#include "lighting-directional.vert"
-#endif
-
-// Skinning
-#if defined(SKINNING)
-#include "skinning.vert"
-#else
-#include "skinning-none.vert" 
-#endif
-
-
-void main()
-{
-    // Get the position and normal
-    vec4 position = getPosition();
-    vec3 normal = getNormal();
-
-    // Transform position to clip space.
-    gl_Position = u_worldViewProjectionMatrix * position;
-
-    // Transform normal to view space.
-    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
-    v_normalVector = inverseTransposeWorldViewMatrix * normal;
-
-    // Apply light.
-    applyLight(position);
-    
-    // Pass the vertex color to fragment shader
-    #if defined(VERTEX_COLOR)
-	v_color = a_color;
-    #endif
-}
+#ifndef DIRECTIONAL_LIGHT_COUNT
+#define DIRECTIONAL_LIGHT_COUNT 0
+#endif
+#ifndef SPOT_LIGHT_COUNT
+#define SPOT_LIGHT_COUNT 0
+#endif
+#ifndef POINT_LIGHT_COUNT
+#define POINT_LIGHT_COUNT 0
+#endif
+#if (DIRECTIONAL_LIGHT_COUNT > 0) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+#define LIGHTING
+#endif
+
+///////////////////////////////////////////////////////////
+// Attributes
+attribute vec4 a_position;
+
+#if defined(SKINNING)
+attribute vec4 a_blendWeights;
+attribute vec4 a_blendIndices;
+#endif
+
+#if defined(LIGHTMAP)
+attribute vec2 a_texCoord1;
+#endif
+
+#if defined(LIGHTING)
+attribute vec3 a_normal;
+#endif
+
+#if defined(VERTEX_COLOR)
+attribute vec3 a_color;
+#endif
+
+///////////////////////////////////////////////////////////
+// Uniforms
+uniform mat4 u_worldViewProjectionMatrix;
+
+#if defined(SKINNING)
+uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];
+#endif
+
+#if defined(LIGHTING)
+uniform mat4 u_inverseTransposeWorldViewMatrix;
+
+#if (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0) || defined(SPECULAR)
+uniform mat4 u_worldViewMatrix;
+#endif
+
+#if (DIRECTIONAL_LIGHT_COUNT > 0)
+uniform vec3 u_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+
+#if (POINT_LIGHT_COUNT > 0) 
+uniform vec3 u_pointLightPosition[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+uniform vec3 u_spotLightPosition[SPOT_LIGHT_COUNT];
+uniform vec3 u_spotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+
+#if defined(SPECULAR)
+uniform vec3 u_cameraPosition;
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////
+// Varyings
+#if defined(LIGHTMAP)
+varying vec2 v_texCoord1;
+#endif
+
+#if defined(VERTEX_COLOR)
+varying vec3 v_color;
+#endif
+
+#if defined(LIGHTING)
+
+varying vec3 v_normalVector;
+
+#if (DIRECTIONAL_LIGHT_COUNT > 0) 
+varying vec3 v_lightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+
+#if (POINT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToPointLightDirection[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToSpotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+
+#if defined(SPECULAR)
+varying vec3 v_cameraDirection;
+#endif
+
+#include "lighting.vert"
+
+#endif
+
+#if defined(SKINNING)
+#include "skinning.vert"
+#else
+#include "skinning-none.vert" 
+#endif
+
+
+void main()
+{
+    vec4 position = getPosition();
+    gl_Position = u_worldViewProjectionMatrix * position;
+
+    #if defined (LIGHTING)
+
+    vec3 normal = getNormal();
+
+    // Transform normal to view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
+    v_normalVector = inverseTransposeWorldViewMatrix * normal;
+
+    // Apply light.
+    applyLight(position);
+
+    #endif
+
+    // Pass the lightmap texture coordinate
+    #if defined(LIGHTMAP)
+    v_texCoord1 = a_texCoord1;
+    #endif
+    
+    // Pass the vertex color
+    #if defined(VERTEX_COLOR)
+	v_color = a_color;
+    #endif
+}

+ 24 - 1
gameplay/res/shaders/font.frag

@@ -1,17 +1,40 @@
 #ifdef OPENGL_ES
+#extension GL_OES_standard_derivatives : enable
+#ifdef GL_FRAGMENT_PRECISION_HIGH
 precision highp float;
+#else
+precision mediump float;
+#endif
 #endif
 
+///////////////////////////////////////////////////////////
 // Uniforms
 uniform sampler2D u_texture;
 
+#ifdef DISTANCE_FIELD
+uniform vec2 u_cutoff;
+#endif
+
+///////////////////////////////////////////////////////////
 // Varyings
 varying vec2 v_texCoord;
 varying vec4 v_color;
 
 
 void main()
-{
+{ 
+    #ifdef DISTANCE_FIELD
+    
+    gl_FragColor = v_color;
+    float distance = texture2D(u_texture, v_texCoord).a;
+    float smoothing = fwidth(distance);
+    float alpha = smoothstep(0.5 - smoothing * u_cutoff.x, 0.5 + smoothing * u_cutoff.y, distance);
+    gl_FragColor.a = alpha * v_color.a;
+    
+    #else
+    
     gl_FragColor = v_color;
     gl_FragColor.a = texture2D(u_texture, v_texCoord).a * v_color.a;
+    
+    #endif
 }

+ 3 - 0
gameplay/res/shaders/font.vert

@@ -1,11 +1,14 @@
+///////////////////////////////////////////////////////////
 // Atttributes
 attribute vec3 a_position;
 attribute vec2 a_texCoord;
 attribute vec4 a_color;
 
+///////////////////////////////////////////////////////////
 // Uniforms
 uniform mat4 u_projectionMatrix;
 
+///////////////////////////////////////////////////////////
 // Varyings
 varying vec2 v_texCoord;
 varying vec4 v_color;

+ 0 - 15
gameplay/res/shaders/form.frag

@@ -1,15 +0,0 @@
-#ifdef OPENGL_ES
-precision highp float;
-#endif
-
-// Uniforms
-uniform sampler2D u_texture;
-
-// Varyings
-varying vec2 v_texCoord;
-
-
-void main()
-{
-    gl_FragColor = texture2D(u_texture, v_texCoord);
-}

+ 0 - 16
gameplay/res/shaders/form.vert

@@ -1,16 +0,0 @@
-// Attributes
-attribute vec3 a_position;
-attribute vec2 a_texCoord;
-
-// Uniforms
-uniform mat4 u_worldViewProjectionMatrix;
-
-// Varyings
-varying vec2 v_texCoord;
-
-
-void main()
-{
-    gl_Position = u_worldViewProjectionMatrix * vec4(a_position, 1);
-    v_texCoord = a_texCoord;
-}

+ 0 - 41
gameplay/res/shaders/lighting-directional.frag

@@ -1,41 +0,0 @@
-#if defined(BUMPED)
-
-vec3 getLitPixel()
-{
-    // Fetch normals from the normal map
-    vec3 normalVector = normalize(texture2D(u_normalmapTexture, v_texCoord).rgb * 2.0 - 1.0);
-    vec3 lightDirection = normalize(v_lightDirection);
-    
-    #if defined(SPECULAR)
-    
-    vec3 cameraDirection = normalize(v_cameraDirection);
-    return computeLighting(normalVector, -lightDirection, 1.0, cameraDirection);
-    
-    #else
-    
-    return computeLighting(normalVector, -lightDirection, 1.0);
-    
-    #endif
-}
-
-#else
-
-vec3 getLitPixel()
-{
-    // Normalize the vectors.
-    vec3 normalVector = normalize(v_normalVector);
-    vec3 lightDirection = normalize(u_lightDirection);
-
-    #if defined(SPECULAR)
-    
-    vec3 cameraDirection = normalize(v_cameraDirection);
-    return computeLighting(normalVector, -lightDirection, 1.0, cameraDirection);
-    
-    #else
-    
-    return computeLighting(normalVector, -lightDirection, 1.0);
-    
-    #endif
-}
-
-#endif

+ 0 - 31
gameplay/res/shaders/lighting-directional.vert

@@ -1,31 +0,0 @@
-#if defined(BUMPED)
-
-void applyLight(mat3 tangentSpaceTransformMatrix)
-{
-    // Transform light direction to tangent space
-    v_lightDirection = tangentSpaceTransformMatrix * u_lightDirection;
-    
-    #if defined(SPECULAR)
-
-    // Compute the camera direction for specular lighting
-	vec4 positionWorldViewSpace = u_worldViewMatrix * a_position;
-    v_cameraDirection = u_cameraPosition - positionWorldViewSpace.xyz;
-
-    #endif
-}
-
-#else
-
-void applyLight(vec4 position)
-{
-    
-    #if defined(SPECULAR)
-
-    // Compute the camera direction for specular lighting
-	vec4 positionWorldViewSpace = u_worldViewMatrix * position;
-    v_cameraDirection = u_cameraPosition - positionWorldViewSpace.xyz;
-
-    #endif
-}
-
-#endif

+ 0 - 46
gameplay/res/shaders/lighting-point.frag

@@ -1,46 +0,0 @@
-#if defined(BUMPED)
-
-vec3 getLitPixel()
-{
-    // Fetch normals from the normal map and normalize the vectors
-    vec3 normalVector = normalize(texture2D(u_normalmapTexture, v_texCoord).rgb * 2.0 - 1.0);
-    vec3 vertexToPointLightDirection = normalize(v_vertexToPointLightDirection);
-    
-    float pointLightAttenuation = clamp(v_pointLightAttenuation, 0.0, 1.0);
-    
-    #if defined(SPECULAR)
-    
-    vec3 cameraDirection = normalize(v_cameraDirection);
-    return computeLighting(normalVector, vertexToPointLightDirection, pointLightAttenuation, cameraDirection);
-    
-    #else
-    
-    return computeLighting(normalVector, vertexToPointLightDirection, pointLightAttenuation);
-    
-    #endif
-}
-
-#else
-
-vec3 getLitPixel()
-{
-    // Normalize the vectors.
-    vec3 normalVector = normalize(v_normalVector);    
-    vec3 vertexToPointLightDirection = normalize(v_vertexToPointLightDirection);
-    
-    // Fetch point light attenuation.
-    float pointLightAttenuation = v_pointLightAttenuation;
-    
-    #if defined (SPECULAR)
-    
-    vec3 cameraDirection = normalize(v_cameraDirection);    
-    return computeLighting(normalVector, vertexToPointLightDirection, pointLightAttenuation, cameraDirection);
-    
-    #else
-    
-    return computeLighting(normalVector, vertexToPointLightDirection, pointLightAttenuation);
-    
-    #endif
-}
-
-#endif

+ 0 - 50
gameplay/res/shaders/lighting-point.vert

@@ -1,50 +0,0 @@
-#if defined(BUMPED)
-
-void applyLight(mat3 tangentSpaceTransformMatrix)
-{
-    vec4 positionWorldViewSpace = u_worldViewMatrix * a_position;
-    
-    // Compute the light direction with light position and the vertex position.
-    vec3 lightDirection = u_pointLightPosition - positionWorldViewSpace.xyz;
-    
-    // Transform current light direction to tangent space.
-    vec3 vertexToPointLightDirection = tangentSpaceTransformMatrix * lightDirection;
-
-    // Attenuation
-    v_pointLightAttenuation = 1.0 - dot(lightDirection * u_pointLightRangeInverse, lightDirection * u_pointLightRangeInverse);
-
-    // Output light direction.
-    v_vertexToPointLightDirection =  vertexToPointLightDirection;
-    
-    #if defined (SPECULAR)
- 
-    // Compute camera direction and transform it to tangent space.
-    v_cameraDirection = tangentSpaceTransformMatrix * (u_cameraPosition - positionWorldViewSpace.xyz);
-    
-    #endif
-}
-
-#else
-
-void applyLight(vec4 position)
-{
-    // World view space position.
-	vec4 positionWorldViewSpace = u_worldViewMatrix * position;
-    
-    // Compute the light direction with light position and the vertex position.
-	v_vertexToPointLightDirection = u_pointLightPosition - positionWorldViewSpace.xyz;
-   
-    // Attenuation
-    v_pointLightAttenuation = 1.0 - dot(v_vertexToPointLightDirection * u_pointLightRangeInverse, v_vertexToPointLightDirection * u_pointLightRangeInverse);
-
-    // Output light direction.
-    //v_vertexToPointLightDirection = lightDirection;
-   
-    #if defined (SPECULAR)  
-
-	v_cameraDirection = u_cameraPosition - positionWorldViewSpace.xyz;
-	
-    #endif
-}
-
-#endif

+ 0 - 63
gameplay/res/shaders/lighting-spot.frag

@@ -1,63 +0,0 @@
-float lerpstep( float lower, float upper, float s)
-{
-    return clamp( ( s - lower ) / ( upper - lower ), 0.0, 1.0 );
-}
-
-#if defined(BUMPED)
-
-vec3 getLitPixel()
-{
-    // Fetch normals from the normal map.
-    vec3 normalVector = normalize(texture2D(u_normalmapTexture, v_texCoord).rgb * 2.0 - 1.0);
-    vec3 spotLightDirection = normalize(v_spotLightDirection);
-    vec3 vertexToSpotLightDirection = normalize(v_vertexToSpotLightDirection);
-    
-    // "-lightDirection" because light direction points in opposite direction to to spot direction.
-    float spotCurrentAngleCos = dot(spotLightDirection, -vertexToSpotLightDirection);
-    
-    // Intensity of spot depends on the spot light attenuation and the part of the cone vertexToSpotLightDirection points to (inner or outer).
-    float spotLightAttenuation = clamp(v_spotLightAttenuation, 0.0, 1.0);
-    spotLightAttenuation *= lerpstep(u_spotLightOuterAngleCos, u_spotLightInnerAngleCos, spotCurrentAngleCos);
-
-    #if defined(SPECULAR)
-    
-    vec3 cameraDirection = normalize(v_cameraDirection);
-    return computeLighting(normalVector, vertexToSpotLightDirection, spotLightAttenuation, cameraDirection);
-    
-    #else
-    
-    return computeLighting(normalVector, vertexToSpotLightDirection, spotLightAttenuation);
-    
-    #endif
-}
-
-#else
-
-vec3 getLitPixel()
-{
-    // Normalize the vectors.
-    vec3 normalVector = normalize(v_normalVector);
-    vec3 spotLightDirection = normalize(u_spotLightDirection); 
-    vec3 vertexToSpotLightDirection = normalize(v_vertexToSpotLightDirection);
-
-    // "-lightDirection" is used because light direction points in opposite direction to spot direction.
-    float spotCurrentAngleCos = dot(spotLightDirection, -vertexToSpotLightDirection);
-    
-    // Intensity of spot depends on the spot light attenuation and the 
-    // part of the cone vertexToSpotLightDirection points to (inner or outer).
-    float spotLightAttenuation = clamp(v_spotLightAttenuation, 0.0, 1.0);
-    spotLightAttenuation *= lerpstep(u_spotLightOuterAngleCos, u_spotLightInnerAngleCos, spotCurrentAngleCos);
-
-    #if defined(SPECULAR)
-    
-    vec3 cameraDirection = normalize(v_cameraDirection);
-    return computeLighting(normalVector, vertexToSpotLightDirection, spotLightAttenuation, cameraDirection);
-    
-    #else
-    
-    return computeLighting(normalVector, vertexToSpotLightDirection, spotLightAttenuation);
-    
-    #endif
-}
-
-#endif

+ 0 - 52
gameplay/res/shaders/lighting-spot.vert

@@ -1,52 +0,0 @@
-#if defined(BUMPED)
-
-void applyLight(mat3 tangentSpaceTransformMatrix)
-{
-    //vec4 positionWorldSpace = u_worldMatrix * a_position;
-	vec4 positionWorldViewSpace = u_worldViewMatrix * a_position;
-
-    // Transform spot light direction to tangent space.
-    v_spotLightDirection = tangentSpaceTransformMatrix * u_spotLightDirection;
-
-    // Compute the light direction with light position and the vertex position.
-	vec3 lightDirection = u_spotLightPosition - positionWorldViewSpace.xyz;
-    
-    // Transform current light direction to tangent space.
-    lightDirection = tangentSpaceTransformMatrix * lightDirection;
-    
-    // Attenuation
-    v_spotLightAttenuation = 1.0 - dot(lightDirection * u_spotLightRangeInverse, lightDirection * u_spotLightRangeInverse);
-
-    // Output light direction.
-    v_vertexToSpotLightDirection = lightDirection;
-    
-    #if defined(SPECULAR)
-    
-    // Compute camera direction and transform it to tangent space.
-	v_cameraDirection = tangentSpaceTransformMatrix * (u_cameraPosition - positionWorldViewSpace.xyz);
-    
-    #endif
-}
-
-#else
-
-void applyLight(vec4 position)
-{
-    // World view space position.
-	vec4 positionWorldViewSpace = u_worldViewMatrix * position;
-
-    // Compute the light direction with light position and the vertex position.
-	v_vertexToSpotLightDirection = u_spotLightPosition - positionWorldViewSpace.xyz;
-    
-    // Attenuation
-    v_spotLightAttenuation = 1.0 - dot(v_vertexToSpotLightDirection * u_spotLightRangeInverse, v_vertexToSpotLightDirection * u_spotLightRangeInverse);
-  
-    // Compute camera direction and transform it to tangent space.
-	#if defined(SPECULAR)
-	
-	v_cameraDirection = u_cameraPosition - positionWorldViewSpace.xyz;
-	
-	#endif
-}
-
-#endif

+ 93 - 46
gameplay/res/shaders/lighting.frag

@@ -1,46 +1,93 @@
-vec4 _baseColor;
-vec3 _ambientColor;
-vec3 _diffuseColor;
-
-#if defined(SPECULAR)
-
-vec3 _specularColor;
-
-vec3 computeLighting(vec3 normalVector, vec3 lightDirection, float attenuation, vec3 cameraDirection)
-{
-    // Ambient
-    _ambientColor = _baseColor.rgb * u_ambientColor;
-
-    // Diffuse
-    float ddot = dot(normalVector, lightDirection);
-    float diffuseIntensity = attenuation * ddot;
-    diffuseIntensity = max(0.0, diffuseIntensity);
-    _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
-
-    // Specular
-    vec3 halfVector = normalize(lightDirection + cameraDirection);
-    float specularIntensity = attenuation * max(0.0, pow(dot(normalVector, halfVector), u_specularExponent));
-    specularIntensity = max(0.0, specularIntensity);
-    _specularColor = u_lightColor * _baseColor.rgb * specularIntensity;
-	
-	return _ambientColor + _diffuseColor + _specularColor;
-}
-
-#else
-
-vec3 computeLighting(vec3 normalVector, vec3 lightDirection, float attenuation)
-{
-    // Ambient
-    _ambientColor = _baseColor.rgb * u_ambientColor;
-
-    // Diffuse
-	float ddot = dot(normalVector, lightDirection);
-    float diffuseIntensity = attenuation * ddot;
-    diffuseIntensity = max(0.0, diffuseIntensity);
-    _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
-	
-	return _ambientColor + _diffuseColor;
-}
-
-#endif
-
+
+vec3 computeLighting(vec3 normalVector, vec3 lightDirection, vec3 lightColor, float attenuation)
+{
+    float diffuse = max(dot(normalVector, lightDirection), 0.0);
+     vec3 diffuseColor = lightColor * _baseColor.rgb * diffuse * attenuation;
+
+    #if defined(SPECULAR)
+
+	// Phong shading
+    //vec3 vertexToEye = normalize(v_cameraDirection);
+    //vec3 specularAngle = normalize(normalVector * diffuse * 2.0 - lightDirection);  
+    //vec3 specularColor = vec3(pow(clamp(dot(specularAngle, vertexToEye), 0.0, 1.0), u_specularExponent)); 
+
+    // Blinn-Phong shading
+    vec3 vertexToEye = normalize(v_cameraDirection);
+    vec3 halfVector = normalize(lightDirection + vertexToEye);
+    float specularAngle = clamp(dot(normalVector, halfVector), 0.0, 1.0);
+    vec3 specularColor = vec3(pow(specularAngle, u_specularExponent)) * attenuation;
+
+    return diffuseColor + specularColor;
+
+    #else
+    
+    return diffuseColor;
+    
+    #endif
+}
+
+vec3 getLitPixel()
+{
+    #if defined(BUMPED)
+    
+    vec3 normalVector = normalize(texture2D(u_normalmapTexture, v_texCoord).rgb * 2.0 - 1.0);
+    
+    #else
+    
+    vec3 normalVector = normalize(v_normalVector);
+    
+    #endif
+    
+    vec3 ambientColor = _baseColor.rgb * u_ambientColor;
+    vec3 combinedColor = ambientColor;
+
+    // Directional light contribution
+    #if (DIRECTIONAL_LIGHT_COUNT > 0)
+    for (int i = 0; i < DIRECTIONAL_LIGHT_COUNT; ++i)
+    {
+        #if defined(BUMPED)
+        vec3 lightDirection = normalize(v_directionalLightDirection[i]);
+        #else
+        vec3 lightDirection = normalize(u_directionalLightDirection[i]);
+        #endif 
+        combinedColor += computeLighting(normalVector, -lightDirection, u_directionalLightColor[i], 1.0);
+    }
+    #endif
+
+    // Point light contribution
+    #if (POINT_LIGHT_COUNT > 0)
+    for (int i = 0; i < POINT_LIGHT_COUNT; ++i)
+    {
+        vec3 ldir = v_vertexToPointLightDirection[i] * u_pointLightRangeInverse[i];
+        float attenuation = clamp(1.0 - dot(ldir, ldir), 0.0, 1.0);
+        combinedColor += computeLighting(normalVector, normalize(v_vertexToPointLightDirection[i]), u_pointLightColor[i], attenuation);
+    }
+    #endif
+
+    // Spot light contribution
+    #if (SPOT_LIGHT_COUNT > 0)
+    for (int i = 0; i < SPOT_LIGHT_COUNT; ++i)
+    {
+        // Compute range attenuation
+        vec3 ldir = v_vertexToSpotLightDirection[i] * u_spotLightRangeInverse[i];
+        float attenuation = clamp(1.0 - dot(ldir, ldir), 0.0, 1.0);
+        vec3 vertexToSpotLightDirection = normalize(v_vertexToSpotLightDirection[i]);
+
+        // TODO: Let app normalize this! Need Node::getForwardVectorViewNorm
+        #if defined(BUMPED)
+            vec3 spotLightDirection = normalize(v_spotLightDirection[i]);
+        #else
+            vec3 spotLightDirection = normalize(u_spotLightDirection[i]);
+        #endif
+
+        // "-lightDirection" is used because light direction points in opposite direction to spot direction.
+        float spotCurrentAngleCos = dot(spotLightDirection, -vertexToSpotLightDirection);
+
+		// Apply spot attenuation
+        attenuation *= smoothstep(u_spotLightOuterAngleCos[i], u_spotLightInnerAngleCos[i], spotCurrentAngleCos);
+        combinedColor += computeLighting(normalVector, vertexToSpotLightDirection, u_spotLightColor[i], attenuation);
+    }
+    #endif
+
+    return combinedColor;
+}

+ 67 - 0
gameplay/res/shaders/lighting.vert

@@ -0,0 +1,67 @@
+
+#if defined(BUMPED)
+void applyLight(vec4 position, mat3 tangentSpaceTransformMatrix)
+{
+    #if (defined(SPECULAR) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0))
+    vec4 positionWorldViewSpace = u_worldViewMatrix * position;
+    #endif
+    
+    #if (DIRECTIONAL_LIGHT_COUNT > 0)
+    for (int i = 0; i < DIRECTIONAL_LIGHT_COUNT; ++i)
+    {
+        // Transform light direction to tangent space
+        v_directionalLightDirection[i] = tangentSpaceTransformMatrix * u_directionalLightDirection[i];
+    }
+    #endif
+    
+    #if (POINT_LIGHT_COUNT > 0)
+    for (int i = 0; i < POINT_LIGHT_COUNT; ++i)
+    {
+        // Compute the vertex to light direction, in tangent space
+        v_vertexToPointLightDirection[i] = tangentSpaceTransformMatrix * (u_pointLightPosition[i] - positionWorldViewSpace.xyz);
+    }
+    #endif
+    
+    #if (SPOT_LIGHT_COUNT > 0)
+    for (int i = 0; i < SPOT_LIGHT_COUNT; ++i)
+    {
+        // Compute the vertex to light direction, in tangent space
+	    v_vertexToSpotLightDirection[i] = tangentSpaceTransformMatrix * (u_spotLightPosition[i] - positionWorldViewSpace.xyz);
+        v_spotLightDirection[i] = tangentSpaceTransformMatrix * u_spotLightDirection[i];
+    }
+    #endif
+    
+    #if defined(SPECULAR)
+    // Compute camera direction and transform it to tangent space.
+    v_cameraDirection = tangentSpaceTransformMatrix * (u_cameraPosition - positionWorldViewSpace.xyz);
+    #endif
+}
+#else
+void applyLight(vec4 position)
+{
+    #if defined(SPECULAR) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+	vec4 positionWorldViewSpace = u_worldViewMatrix * position;
+    #endif
+
+    #if (POINT_LIGHT_COUNT > 0)
+    for (int i = 0; i < POINT_LIGHT_COUNT; ++i)
+    {
+        // Compute the light direction with light position and the vertex position.
+        v_vertexToPointLightDirection[i] = u_pointLightPosition[i] - positionWorldViewSpace.xyz;
+    }
+    #endif
+
+    #if (SPOT_LIGHT_COUNT > 0)
+    for (int i = 0; i < SPOT_LIGHT_COUNT; ++i)
+    {
+        // Compute the light direction with light position and the vertex position.
+	    v_vertexToSpotLightDirection[i] = u_spotLightPosition[i] - positionWorldViewSpace.xyz;
+    }
+    #endif
+
+    #if defined(SPECULAR)  
+	v_cameraDirection = u_cameraPosition - positionWorldViewSpace.xyz;
+    #endif
+}
+
+#endif

+ 24 - 26
gameplay/res/shaders/skinning-none.vert

@@ -1,27 +1,25 @@
-vec4 getPosition()
-{
-    return a_position;    
-}
-
-#if defined(LIGHTING)
-
-vec3 getNormal()
-{
-    return a_normal;
-}
-
-#if defined(BUMPED)
-
-vec3 getTangent()
-{
-    return a_tangent;
-}
-
-vec3 getBinormal()
-{
-    return a_binormal;
-}
-
-#endif
-
+vec4 getPosition()
+{
+    return a_position;    
+}
+
+#if defined(LIGHTING)
+
+vec3 getNormal()
+{
+    return a_normal;
+}
+
+#if defined(BUMPED)
+vec3 getTangent()
+{
+    return a_tangent;
+}
+
+vec3 getBinormal()
+{
+    return a_binormal;
+}
+#endif
+
 #endif

+ 84 - 93
gameplay/res/shaders/skinning.vert

@@ -1,93 +1,84 @@
-vec4 _skinnedPosition;
-#if defined(LIGHTING)
-vec3 _skinnedNormal;
-#endif
-
-void skinPosition(float blendWeight, int matrixIndex)
-{
-    vec4 tmp;
-    tmp.x = dot(a_position, u_matrixPalette[matrixIndex]);
-    tmp.y = dot(a_position, u_matrixPalette[matrixIndex + 1]);
-    tmp.z = dot(a_position, u_matrixPalette[matrixIndex + 2]);
-    tmp.w = a_position.w;
-    _skinnedPosition += blendWeight * tmp;
-}
-
-vec4 getPosition()
-{
-    _skinnedPosition = vec4(0.0);
-
-    // Transform position to view space using matrix palette with four matrices used to transform a vertex.
-    float blendWeight = a_blendWeights[0];
-    int matrixIndex = int (a_blendIndices[0]) * 3;
-    skinPosition(blendWeight, matrixIndex);
-
-    blendWeight = a_blendWeights[1];
-    matrixIndex = int(a_blendIndices[1]) * 3;
-    skinPosition(blendWeight, matrixIndex);
-
-    blendWeight = a_blendWeights[2];
-    matrixIndex = int(a_blendIndices[2]) * 3;
-    skinPosition(blendWeight, matrixIndex);
-
-    blendWeight = a_blendWeights[3];
-    matrixIndex = int(a_blendIndices[3]) * 3;
-    skinPosition(blendWeight, matrixIndex);
-
-    return _skinnedPosition;    
-}
-
-#if defined(LIGHTING)
-
-void skinTangentSpaceVector(vec3 vector, float blendWeight, int matrixIndex)
-{
-    vec3 tmp;
-    tmp.x = dot(vector, u_matrixPalette[matrixIndex].xyz);
-    tmp.y = dot(vector, u_matrixPalette[matrixIndex + 1].xyz);
-    tmp.z = dot(vector, u_matrixPalette[matrixIndex + 2].xyz);
-    _skinnedNormal += blendWeight * tmp;
-}
-
-vec3 getTangentSpaceVector(vec3 vector)
-{
-    _skinnedNormal = vec3(0.0);
-
-    // Transform normal to view space using matrix palette with four matrices used to transform a vertex.
-    float blendWeight = a_blendWeights[0];
-    int matrixIndex = int (a_blendIndices[0]) * 3;
-    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
-
-    blendWeight = a_blendWeights[1];
-    matrixIndex = int(a_blendIndices[1]) * 3;
-    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
-
-    blendWeight = a_blendWeights[2];
-    matrixIndex = int(a_blendIndices[2]) * 3;
-    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
-
-    blendWeight = a_blendWeights[3];
-    matrixIndex = int(a_blendIndices[3]) * 3;
-    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
-
-    return _skinnedNormal;
-}
-
-vec3 getNormal()
-{
-    return getTangentSpaceVector(a_normal);
-}
-
-#if defined(BUMPED)
-
-vec3 getTangent()
-{
-    return getTangentSpaceVector(a_tangent);
-}
-
-vec3 getBinormal()
-{
-    return getTangentSpaceVector(a_binormal);
-}
-
-#endif
-#endif
+
+vec4 _skinnedPosition;
+
+void skinPosition(float blendWeight, int matrixIndex)
+{
+    vec4 tmp;
+    tmp.x = dot(a_position, u_matrixPalette[matrixIndex]);
+    tmp.y = dot(a_position, u_matrixPalette[matrixIndex + 1]);
+    tmp.z = dot(a_position, u_matrixPalette[matrixIndex + 2]);
+    tmp.w = a_position.w;
+    _skinnedPosition += blendWeight * tmp;
+}
+
+vec4 getPosition()
+{
+    _skinnedPosition = vec4(0.0);
+    float blendWeight = a_blendWeights[0];
+    int matrixIndex = int (a_blendIndices[0]) * 3;
+    skinPosition(blendWeight, matrixIndex);
+    blendWeight = a_blendWeights[1];
+    matrixIndex = int(a_blendIndices[1]) * 3;
+    skinPosition(blendWeight, matrixIndex);
+    blendWeight = a_blendWeights[2];
+    matrixIndex = int(a_blendIndices[2]) * 3;
+    skinPosition(blendWeight, matrixIndex);
+    blendWeight = a_blendWeights[3];
+    matrixIndex = int(a_blendIndices[3]) * 3;
+    skinPosition(blendWeight, matrixIndex);
+    return _skinnedPosition;    
+}
+
+#if defined(LIGHTING)
+
+vec3 _skinnedNormal;
+
+void skinTangentSpaceVector(vec3 vector, float blendWeight, int matrixIndex)
+{
+    vec3 tmp;
+    tmp.x = dot(vector, u_matrixPalette[matrixIndex].xyz);
+    tmp.y = dot(vector, u_matrixPalette[matrixIndex + 1].xyz);
+    tmp.z = dot(vector, u_matrixPalette[matrixIndex + 2].xyz);
+    _skinnedNormal += blendWeight * tmp;
+}
+
+vec3 getTangentSpaceVector(vec3 vector)
+{
+    _skinnedNormal = vec3(0.0);
+    // Transform normal to view space using matrix palette with four matrices used to transform a vertex.
+    float blendWeight = a_blendWeights[0];
+    int matrixIndex = int (a_blendIndices[0]) * 3;
+    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
+    blendWeight = a_blendWeights[1];
+    matrixIndex = int(a_blendIndices[1]) * 3;
+    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
+    blendWeight = a_blendWeights[2];
+    matrixIndex = int(a_blendIndices[2]) * 3;
+    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
+    blendWeight = a_blendWeights[3];
+    matrixIndex = int(a_blendIndices[3]) * 3;
+    skinTangentSpaceVector(vector, blendWeight, matrixIndex);
+    return _skinnedNormal;
+}
+
+vec3 getNormal()
+{
+    return getTangentSpaceVector(a_normal);
+}
+
+#if defined(BUMPED)
+
+vec3 getTangent()
+{
+    return getTangentSpaceVector(a_tangent);
+}
+
+vec3 getBinormal()
+{
+    return getTangentSpaceVector(a_binormal);
+}
+
+#endif
+
+#endif
+

+ 21 - 15
gameplay/res/shaders/sprite.frag

@@ -1,16 +1,22 @@
-#ifdef OPENGL_ES
-precision highp float;
-#endif
-
-// Uniforms
-uniform sampler2D u_texture;
-
-// Varyings
-varying vec2 v_texCoord;
-varying vec4 v_color;
-
-
-void main()
-{
-    gl_FragColor = v_color * texture2D(u_texture, v_texCoord);
+#ifdef OPENGL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+#endif
+
+///////////////////////////////////////////////////////////
+// Uniforms
+uniform sampler2D u_texture;
+
+///////////////////////////////////////////////////////////
+// Varyings
+varying vec2 v_texCoord;
+varying vec4 v_color;
+
+
+void main()
+{
+    gl_FragColor = v_color * texture2D(u_texture, v_texCoord);
 }

+ 3 - 0
gameplay/res/shaders/sprite.vert

@@ -1,11 +1,14 @@
+///////////////////////////////////////////////////////////
 // Attributes
 attribute vec3 a_position;
 attribute vec2 a_texCoord;
 attribute vec4 a_color;
 
+///////////////////////////////////////////////////////////
 // Uniforms
 uniform mat4 u_projectionMatrix;
 
+///////////////////////////////////////////////////////////
 // Varyings
 varying vec2 v_texCoord;
 varying vec4 v_color;

+ 94 - 37
gameplay/res/shaders/terrain.frag

@@ -1,29 +1,81 @@
 #ifdef OPENGL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
 precision highp float;
+#else
+precision mediump float;
+#endif
+#endif
+
+#ifndef DIRECTIONAL_LIGHT_COUNT
+#define DIRECTIONAL_LIGHT_COUNT 0
+#endif
+#ifndef SPOT_LIGHT_COUNT
+#define SPOT_LIGHT_COUNT 0
+#endif
+#ifndef POINT_LIGHT_COUNT
+#define POINT_LIGHT_COUNT 0
+#endif
+#if (DIRECTIONAL_LIGHT_COUNT > 0) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+#define LIGHTING
 #endif
 
+///////////////////////////////////////////////////////////
 // Uniforms
-uniform vec3 u_ambientColor;                    // Ambient color
-uniform vec3 u_lightColor;                      // Light color
-uniform vec3 u_lightDirection;					// Light direction
-#if defined(DEBUG_PATCHES)
-uniform float u_row;                            // Patch row
-uniform float u_column;                         // Patch column
+uniform vec3 u_ambientColor; 
+
+#if defined(LIGHTING)
+
+#if (DIRECTIONAL_LIGHT_COUNT > 0)
+uniform vec3 u_directionalLightColor[DIRECTIONAL_LIGHT_COUNT];
+uniform vec3 u_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
 #endif
-#if (LAYER_COUNT > 0)
-uniform sampler2D u_samplers[SAMPLER_COUNT];    // Surface layer samplers
+
+#if (POINT_LIGHT_COUNT > 0)
+uniform vec3 u_pointLightColor[POINT_LIGHT_COUNT];
+uniform vec3 u_pointLightPosition[POINT_LIGHT_COUNT];
+uniform float u_pointLightRangeInverse[POINT_LIGHT_COUNT];
 #endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+uniform vec3 u_spotLightColor[SPOT_LIGHT_COUNT];
+uniform vec3 u_spotLightDirection[SPOT_LIGHT_COUNT];
+uniform float u_spotLightRangeInverse[SPOT_LIGHT_COUNT];
+uniform float u_spotLightInnerAngleCos[SPOT_LIGHT_COUNT];
+uniform float u_spotLightOuterAngleCos[SPOT_LIGHT_COUNT];
+#endif
+
 #if defined (NORMAL_MAP)
-uniform sampler2D u_normalMap;                  // Normal map
+uniform sampler2D u_normalMap;
+uniform mat4 u_normalMatrix;
+#endif
+
+#endif
+
+#if defined(DEBUG_PATCHES)
+uniform float u_row;
+uniform float u_column;
+#endif
+
+#if (LAYER_COUNT > 0)
+uniform sampler2D u_surfaceLayerMaps[SAMPLER_COUNT];
 #endif
 
+///////////////////////////////////////////////////////////
+// Variables
+vec4 _baseColor;
+
+///////////////////////////////////////////////////////////
 // Varyings
-#if defined(NORMAL_MAP)
-vec3 v_normalVector;                            // Normal vector variable (from normal map)
+#if defined(LIGHTING)
+#if !defined(NORMAL_MAP)
+varying vec3 v_normalVector;
 #else
-varying vec3 v_normalVector;					// Normal vector from vertex shader
+vec3 v_normalVector;
+#endif
 #endif
+
 varying vec2 v_texCoord0;
+
 #if (LAYER_COUNT > 0)
 varying vec2 v_texCoordLayer0;
 #endif
@@ -33,50 +85,55 @@ varying vec2 v_texCoordLayer1;
 #if (LAYER_COUNT > 2)
 varying vec2 v_texCoordLayer2;
 #endif
-
-// Lighting
-#include "lighting.frag"
-#include "lighting-directional.frag"
-
 #if (LAYER_COUNT > 1)
 void blendLayer(sampler2D textureMap, vec2 texCoord, float alphaBlend)
 {
-    // Sample full intensity diffuse color
     vec3 diffuse = texture2D(textureMap,  mod(texCoord, vec2(1,1))).rgb;
-
     _baseColor.rgb = _baseColor.rgb * (1.0 - alphaBlend) + diffuse * alphaBlend;
 }
 #endif
 
+#if defined(LIGHTING)
+#include "lighting.frag"
+#endif
+
+
 void main()
 {
-#if (LAYER_COUNT > 0)
+    #if (LAYER_COUNT > 0)
     // Sample base texture
-	_baseColor.rgb = texture2D(u_samplers[TEXTURE_INDEX_0], mod(v_texCoordLayer0, vec2(1,1))).rgb;
+	_baseColor.rgb = texture2D(u_surfaceLayerMaps[TEXTURE_INDEX_0], mod(v_texCoordLayer0, vec2(1,1))).rgb;
     _baseColor.a = 1.0;
-#else
+    #else
     // If no layers are defined, simply use a white color
-    _baseColor = vec4(1,1,1,1);
-#endif
+    _baseColor = vec4(1, 1, 1, 1);
+    #endif
 
-#if (LAYER_COUNT > 1)
-    blendLayer(u_samplers[TEXTURE_INDEX_1], v_texCoordLayer1, texture2D(u_samplers[BLEND_INDEX_1], v_texCoord0)[BLEND_CHANNEL_1]);
-#endif
-#if (LAYER_COUNT > 2)
-    blendLayer(u_samplers[TEXTURE_INDEX_2], v_texCoordLayer2, texture2D(u_samplers[BLEND_INDEX_2], v_texCoord0)[BLEND_CHANNEL_2]);
-#endif
+    #if (LAYER_COUNT > 1)
+    blendLayer(u_surfaceLayerMaps[TEXTURE_INDEX_1], v_texCoordLayer1, texture2D(u_surfaceLayerMaps[BLEND_INDEX_1], v_texCoord0)[BLEND_CHANNEL_1]);
+    #endif
+    #if (LAYER_COUNT > 2)
+    blendLayer(u_surfaceLayerMaps[TEXTURE_INDEX_2], v_texCoordLayer2, texture2D(u_surfaceLayerMaps[BLEND_INDEX_2], v_texCoord0)[BLEND_CHANNEL_2]);
+    #endif
 
-#if defined(DEBUG_PATCHES)
-    // If patch debug drawing is enabled, tint patches alternate colors
+    #if defined(DEBUG_PATCHES)
     float tint = mod(u_row + mod(u_column, 2.0), 2.0);
     _baseColor.rgb = _baseColor.rgb * 0.75 + vec3(1.0-tint, tint, 0) * 0.25;
-#endif
+    #endif
 
-    // Light the pixel
-#if defined(NORMAL_MAP)
-    v_normalVector = normalize(texture2D(u_normalMap, v_texCoord0).xyz * 2.0 - 1.0);
-#endif
+    #if defined(LIGHTING)
+
+    #if defined(NORMAL_MAP)
+    v_normalVector = texture2D(u_normalMap, v_texCoord0).xyz * 2.0 - 1.0;
+    v_normalVector = (u_normalMatrix * vec4(v_normalVector.x, v_normalVector.y, v_normalVector.z, 0)).xyz;
+    #endif
 
     gl_FragColor.a = _baseColor.a;
     gl_FragColor.rgb = getLitPixel();
+
+    #else
+
+    gl_FragColor.rgb = _baseColor;
+
+    #endif
 }

+ 80 - 19
gameplay/res/shaders/terrain.vert

@@ -1,21 +1,76 @@
+#ifndef DIRECTIONAL_LIGHT_COUNT
+#define DIRECTIONAL_LIGHT_COUNT 0
+#endif
+#ifndef SPOT_LIGHT_COUNT
+#define SPOT_LIGHT_COUNT 0
+#endif
+#ifndef POINT_LIGHT_COUNT
+#define POINT_LIGHT_COUNT 0
+#endif
+#if (DIRECTIONAL_LIGHT_COUNT > 0) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+#define LIGHTING
+#endif
+
+///////////////////////////////////////////////////////////
 // Attributes
-attribute vec4 a_position;									// Vertex Position							(x, y, z, w)
-#ifndef NORMAL_MAP
-attribute vec3 a_normal;									// Vertex Normal							(x, y, z)
+attribute vec4 a_position;
+#if !defined(NORMAL_MAP) && defined(LIGHTING)
+attribute vec3 a_normal;
 #endif
 attribute vec2 a_texCoord0;
 
+///////////////////////////////////////////////////////////
 // Uniforms
-uniform mat4 u_worldViewProjectionMatrix;					// World view projection matrix
-#ifndef NORMAL_MAP
-uniform mat4 u_normalMatrix;					            // Matrix used for normal vector transformation
+uniform mat4 u_worldViewProjectionMatrix;
+#if !defined(NORMAL_MAP) && defined(LIGHTING)
+uniform mat4 u_normalMatrix;
 #endif
-uniform vec3 u_lightDirection;								// Direction of light
 
+#if defined(LIGHTING)
+
+uniform mat4 u_inverseTransposeWorldViewMatrix;
+
+#if (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+uniform mat4 u_worldViewMatrix;
+#endif
+
+#if (DIRECTIONAL_LIGHT_COUNT > 0)
+uniform vec3 u_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+
+#if (POINT_LIGHT_COUNT > 0) 
+uniform vec3 u_pointLightPosition[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+uniform vec3 u_spotLightPosition[SPOT_LIGHT_COUNT];
+uniform vec3 u_spotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////
 // Varyings
-#ifndef NORMAL_MAP
-varying vec3 v_normalVector;								// Normal vector out
+
+#if defined(LIGHTING)
+varying vec3 v_normalVector;
+
+#if (DIRECTIONAL_LIGHT_COUNT > 0) 
+varying vec3 v_lightDirection[DIRECTIONAL_LIGHT_COUNT];
 #endif
+
+#if (POINT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToPointLightDirection[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToSpotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+
+#include "lighting.vert"
+
+#endif
+
 varying vec2 v_texCoord0;
 #if LAYER_COUNT > 0
 varying vec2 v_texCoordLayer0;
@@ -27,27 +82,33 @@ varying vec2 v_texCoordLayer1;
 varying vec2 v_texCoordLayer2;
 #endif
 
+
 void main()
 {
     // Transform position to clip space.
     gl_Position = u_worldViewProjectionMatrix * a_position;
 
-#ifndef NORMAL_MAP
-    // Pass normal to fragment shader
-    v_normalVector = (u_normalMatrix * vec4(a_normal.x, a_normal.y, a_normal.z, 0)).xyz;
-#endif
+    #if defined(LIGHTING)
+
+    #if !defined(NORMAL_MAP) 
+    v_normalVector = normalize((u_normalMatrix * vec4(a_normal.x, a_normal.y, a_normal.z, 0)).xyz);
+    #endif
+
+    applyLight(a_position);
+
+    #endif
 
     // Pass base texture coord
     v_texCoord0 = a_texCoord0;
 
     // Pass repeated texture coordinates for each layer
-#if LAYER_COUNT > 0
+    #if LAYER_COUNT > 0
     v_texCoordLayer0 = a_texCoord0 * TEXTURE_REPEAT_0;
-#endif
-#if LAYER_COUNT > 1
+    #endif
+    #if LAYER_COUNT > 1
     v_texCoordLayer1 = a_texCoord0 * TEXTURE_REPEAT_1;
-#endif
-#if LAYER_COUNT > 2
+    #endif
+    #if LAYER_COUNT > 2
     v_texCoordLayer2 = a_texCoord0 * TEXTURE_REPEAT_2;
-#endif
+    #endif
 }

+ 0 - 76
gameplay/res/shaders/textured-bumped.frag

@@ -1,76 +0,0 @@
-#define LIGHTING
-#define BUMPED
-
-#ifdef OPENGL_ES
-precision highp float;
-#endif
-
-// Uniforms
-uniform sampler2D u_diffuseTexture;        		// Diffuse map texture
-uniform sampler2D u_normalmapTexture;       	// Normalmap texture
-uniform vec3 u_ambientColor;                    // Ambient color
-uniform vec3 u_lightColor;                      // Light color
-uniform vec3 u_lightDirection;					// Light direction
-#if defined(SPECULAR)
-uniform float u_specularExponent;				// Specular exponent.
-#endif
-#if defined (SPOT_LIGHT)
-uniform float u_spotLightInnerAngleCos;			// The bright spot [0.0 - 1.0]
-uniform float u_spotLightOuterAngleCos;			// The soft outer part [0.0 - 1.0]
-#endif
-#if defined(MODULATE_COLOR)
-uniform vec4 u_modulateColor;					// Modulation color
-#endif
-#if defined(MODULATE_ALPHA)
-uniform float u_modulateAlpha;					// Modulation alpha
-#endif
-
-// Varyings
-varying vec3 v_normalVector;					// Normal vector in view space
-varying vec2 v_texCoord;						// Texture Coordinate
-#if defined(POINT_LIGHT)
-varying vec3 v_vertexToPointLightDirection;		// Light direction w.r.t current vertex in tangent space.
-varying float v_pointLightAttenuation;			// Attenuation of point light.
-#elif defined(SPOT_LIGHT)
-varying vec3 v_spotLightDirection;				// Direction of spot light in tangent space.
-varying vec3 v_vertexToSpotLightDirection;		// Direction of the spot light w.r.t current vertex in tangent space.
-varying float v_spotLightAttenuation;			// Attenuation of spot light.
-#else
-varying vec3 v_lightDirection;					// Direction of light in tangent space.
-#endif
-#if defined(SPECULAR)
-varying vec3 v_cameraDirection;                 // Camera direction
-#endif
-
-// Lighting
-#include "lighting.frag"
-#if defined(POINT_LIGHT)
-#include "lighting-point.frag"
-#elif defined(SPOT_LIGHT)
-#include "lighting-spot.frag"
-#else
-#include "lighting-directional.frag"
-#endif
-
-
-void main()
-{
-    // Fetch diffuse color from texture.
-    _baseColor = texture2D(u_diffuseTexture, v_texCoord);
-
-    // Light the pixel
-    gl_FragColor.a = _baseColor.a;
-    #if defined(TEXTURE_DISCARD_ALPHA)
-    if (gl_FragColor.a < 0.5)
-        discard;
-    #endif
-    gl_FragColor.rgb = getLitPixel();
-
-    // Global color modulation
-    #if defined(MODULATE_COLOR)
-    gl_FragColor *= u_modulateColor;
-    #endif
-    #if defined(MODULATE_ALPHA)
-    gl_FragColor.a *= u_modulateAlpha;
-    #endif
-}

+ 0 - 106
gameplay/res/shaders/textured-bumped.vert

@@ -1,106 +0,0 @@
-#define LIGHTING
-#define BUMPED
-
-// Inputs
-attribute vec4 a_position;									// Vertex Position							(x, y, z, w)
-attribute vec3 a_normal;									// Vertex Normal							(x, y, z)
-attribute vec2 a_texCoord;									// Vertex Texture Coordinate				(u, v)
-attribute vec3 a_tangent;									// Vertex Tangent							(x, y, z)
-attribute vec3 a_binormal;									// Vertex Binormal/Bitangent				(x, y, z)
-#if defined(SKINNING)
-attribute vec4 a_blendWeights;								// Vertex blend weight, up to 4				(0, 1, 2, 3) 
-attribute vec4 a_blendIndices;								// Vertex blend index int u_matrixPalette	(0, 1, 2, 3)
-#endif
-
-// 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
-#if defined(SPECULAR) || defined(SPOT_LIGHT) || defined(POINT_LIGHT)
-uniform mat4 u_worldViewMatrix;								// Matrix to tranform a position to view space
-uniform mat4 u_worldMatrix;								    // Matrix to tranform a position to world space
-#endif
-#if defined(SKINNING)
-uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
-#endif
-#if defined(SPECULAR)
-uniform vec3 u_cameraPosition;                 				// Position of the camera in view space
-#endif
-#if defined(TEXTURE_REPEAT)
-uniform vec2 u_textureRepeat;
-#endif
-#if defined(TEXTURE_OFFSET)
-uniform vec2 u_textureOffset;
-#endif
-#if defined(POINT_LIGHT)
-uniform vec3 u_pointLightPosition;							// Position of light
-uniform float u_pointLightRangeInverse;						// Inverse of light range
-#elif defined(SPOT_LIGHT)
-uniform vec3 u_spotLightPosition;							// Position of light
-uniform float u_spotLightRangeInverse;						// Inverse of light range
-uniform vec3 u_spotLightDirection;							// Direction of light
-#else
-uniform vec3 u_lightDirection;								// Direction of light
-#endif
-
-// Varyings
-varying vec3 v_normalVector;								// Normal vector in view space
-varying vec2 v_texCoord;									// Texture Coordinate
-#if defined(SPECULAR)
-varying vec3 v_cameraDirection;								// Direction the camera is looking at in tangent space
-#endif
-
-// Lighting
-#if defined(POINT_LIGHT)
-varying vec3 v_vertexToPointLightDirection;					// Direction of point light w.r.t current vertex in tangent space
-varying float v_pointLightAttenuation;						// Attenuation of point light
-#include "lighting-point.vert"
-#elif defined(SPOT_LIGHT)
-varying vec3 v_vertexToSpotLightDirection;					// Direction of the spot light w.r.t current vertex in tangent space
-varying float v_spotLightAttenuation;						// Attenuation of spot light
-varying vec3 v_spotLightDirection;							// Direction of spot light in tangent space
-#include "lighting-spot.vert"
-#else
-varying vec3 v_lightDirection;								// Direction of light
-#include "lighting-directional.vert"
-#endif
-
-// Skinning
-#if defined(SKINNING)
-#include "skinning.vert"
-#else
-#include "skinning-none.vert" 
-#endif
-
-
-void main()
-{
-    // Get the position, normal, tangents and binormals.
-    vec4 position = getPosition();
-    vec3 normal = getNormal();
-    vec3 tangent = getTangent();
-    vec3 binormal = getBinormal();
-    
-    // Transform position to clip space.
-    gl_Position = u_worldViewProjectionMatrix * position;
-
-    // Transform the normal, tangent and binormals to view space.
-	mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
-    vec3 normalVector = normalize(inverseTransposeWorldViewMatrix * normal);
-    
-    // Create a transform to convert a vector to tangent space.
-    vec3 tangentVector  = normalize(inverseTransposeWorldViewMatrix * tangent);
-    vec3 binormalVector = normalize(inverseTransposeWorldViewMatrix * binormal);
-    mat3 tangentSpaceTransformMatrix = mat3(tangentVector.x, binormalVector.x, normalVector.x, tangentVector.y, binormalVector.y, normalVector.y, tangentVector.z, binormalVector.z, normalVector.z);
-    
-    // Apply light.
-    applyLight(tangentSpaceTransformMatrix);
-    
-    // Texture transformation.
-    v_texCoord = a_texCoord;
-    #if defined(TEXTURE_REPEAT)
-    v_texCoord *= u_textureRepeat;
-    #endif
-    #if defined(TEXTURE_OFFSET)
-    v_texCoord += u_textureOffset;
-    #endif
-}

+ 0 - 47
gameplay/res/shaders/textured-unlit.frag

@@ -1,47 +0,0 @@
-#ifdef OPENGL_ES
-precision highp float;
-#endif
-
-// Uniforms
-uniform sampler2D u_diffuseTexture;     	// Diffuse texture
-#if defined(TEXTURE_LIGHTMAP)
-uniform sampler2D u_lightmapTexture;     	// Lightmap texture
-#endif
-#if defined(MODULATE_COLOR)
-uniform vec4 u_modulateColor;               // Modulation color
-#endif
-#if defined(MODULATE_ALPHA)
-uniform float u_modulateAlpha;              // Modulation alpha
-#endif
-
-// Varyings
-varying vec2 v_texCoord0;                	// Texture coordinate(u, v)
-#if defined(TEXCOORD1)
-varying vec2 v_texCoord1;                   // Second tex coord for multi-texturing
-#endif
-
-
-void main()
-{
-    // Sample the texture for the color
-    gl_FragColor = texture2D(u_diffuseTexture, v_texCoord0);
-    #if defined(TEXTURE_DISCARD_ALPHA)
-    if (gl_FragColor.a < 0.5)
-        discard;
-    #endif
-    #if defined(TEXTURE_LIGHTMAP)
-    #if defined(TEXCOORD1)
-    vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord1);
-    #else
-    vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord0);
-    #endif
-    gl_FragColor.rgb *= lightColor.rgb;
-    #endif
-    // Global color modulation
-    #if defined(MODULATE_COLOR)
-    gl_FragColor *= u_modulateColor;
-    #endif
-    #if defined(MODULATE_ALPHA)
-    gl_FragColor.a *= u_modulateAlpha;
-    #endif
-}

+ 0 - 57
gameplay/res/shaders/textured-unlit.vert

@@ -1,57 +0,0 @@
-// Attributes
-attribute vec4 a_position;									// Vertex Position							(x, y, z, w)
-attribute vec2 a_texCoord0;									// Vertex Texture Coordinate				(u, v)
-#if defined(TEXCOORD1)
-attribute vec2 a_texCoord1;                                 // Second tex coord for multi-texturing
-#endif
-#if defined(SKINNING)
-attribute vec4 a_blendWeights;								// Vertex blend weight, up to 4				(0, 1, 2, 3) 
-attribute vec4 a_blendIndices;								// Vertex blend index int u_matrixPalette	(0, 1, 2, 3)
-#endif
-
-// Uniforms
-uniform mat4 u_worldViewProjectionMatrix;					// Matrix to transform a position to clip space
-#if defined(SKINNING)
-uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
-#endif
-#if defined(TEXTURE_REPEAT)
-uniform vec2 u_textureRepeat;								// Texture repeat for tiling
-#endif
-#if defined(TEXTURE_OFFSET)
-uniform vec2 u_textureOffset;								// Texture offset
-#endif
-
-// Varyings
-varying vec2 v_texCoord0;									// Texture Coordinate
-#if defined(TEXCOORD1)
-varying vec2 v_texCoord1;                                   // Second tex coord for multi-texturing
-#endif
-
-// Skinning 
-#if defined(SKINNING)
-#include "skinning.vert"
-#else
-#include "skinning-none.vert" 
-#endif
-
-
-void main()
-{
-    // Get the vertex position
-    vec4 position = getPosition();
-
-    // Transform position to clip space.
-    gl_Position = u_worldViewProjectionMatrix * position;
-
-    // Texture transformation.
-    v_texCoord0 = a_texCoord0;
-    #if defined(TEXCOORD1)
-    v_texCoord1 = a_texCoord1;
-    #endif
-    #if defined(TEXTURE_REPEAT)
-    v_texCoord0 *= u_textureRepeat;
-    #endif
-    #if defined(TEXTURE_OFFSET)
-    v_texCoord0 += u_textureOffset;
-    #endif
-}

+ 147 - 73
gameplay/res/shaders/textured.frag

@@ -1,73 +1,147 @@
-#define LIGHTING
-
-#ifdef OPENGL_ES
-precision highp float;
-#endif
-
-// Uniforms
-uniform sampler2D u_diffuseTexture;             // Diffuse map texture
-uniform vec3 u_ambientColor;                    // Ambient color
-uniform vec3 u_lightColor;                      // Light color
-uniform vec3 u_lightDirection;					// Light direction
-#if defined(SPECULAR)
-uniform float u_specularExponent;				// Specular exponent
-#endif
-#if defined(MODULATE_COLOR)
-uniform vec4 u_modulateColor;               	// Modulation color
-#endif
-#if defined(MODULATE_ALPHA)
-uniform float u_modulateAlpha;              	// Modulation alpha
-#endif
-
-// Varyings
-varying vec3 v_normalVector;                    // Normal vector in view space
-varying vec2 v_texCoord;                        // Texture coordinate
-#if defined(POINT_LIGHT)
-varying vec3 v_vertexToPointLightDirection;		// Light direction w.r.t current vertex in tangent space.
-varying float v_pointLightAttenuation;			// Attenuation of point light.
-#elif defined(SPOT_LIGHT)
-varying vec3 v_spotLightDirection;				// Direction of spot light in tangent space.
-varying vec3 v_vertexToSpotLightDirection;		// Direction of the spot light w.r.t current vertex in tangent space.
-varying float v_spotLightAttenuation;			// Attenuation of spot light.
-#else
-varying vec3 v_lightDirection;					// Direction of light in tangent space.
-#endif
-#if defined(SPECULAR)
-varying vec3 v_cameraDirection;                 // Camera direction
-#endif
-
-// Lighting 
-#include "lighting.frag"
-#if defined(POINT_LIGHT)
-#include "lighting-point.frag"
-#elif defined(SPOT_LIGHT)
-uniform float u_spotLightInnerAngleCos;			// The bright spot [0.0 - 1.0]
-uniform float u_spotLightOuterAngleCos;			// The soft outer part [0.0 - 1.0]
-uniform vec3 u_spotLightDirection;              // Direction of a spot light source
-#include "lighting-spot.frag"
-#else
-#include "lighting-directional.frag"
-#endif
-
-
-void main()
-{
-    // Sample the diffuse texture for base color
-    _baseColor = texture2D(u_diffuseTexture, v_texCoord);
-
-    // Light the pixel
-    gl_FragColor.a = _baseColor.a;
-    #if defined(TEXTURE_DISCARD_ALPHA)
-    if (gl_FragColor.a < 0.5)
-        discard;
-    #endif
-    gl_FragColor.rgb = getLitPixel();
-	
-	// Global color modulation
-	#if defined(MODULATE_COLOR)
-	gl_FragColor *= u_modulateColor;
-	#endif
-	#if defined(MODULATE_ALPHA)
-    gl_FragColor.a *= u_modulateAlpha;
-    #endif
-}
+#ifdef OPENGL_ES
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+#endif
+
+#ifndef DIRECTIONAL_LIGHT_COUNT
+#define DIRECTIONAL_LIGHT_COUNT 0
+#endif
+#ifndef SPOT_LIGHT_COUNT
+#define SPOT_LIGHT_COUNT 0
+#endif
+#ifndef POINT_LIGHT_COUNT
+#define POINT_LIGHT_COUNT 0
+#endif
+#if (DIRECTIONAL_LIGHT_COUNT > 0) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+#define LIGHTING
+#endif
+
+///////////////////////////////////////////////////////////
+// Uniforms
+uniform vec3 u_ambientColor;
+
+uniform sampler2D u_diffuseTexture;
+
+#if defined(LIGHTMAP)
+uniform sampler2D u_lightmapTexture;
+#endif
+
+#if defined(LIGHTING)
+
+#if defined(BUMPED)
+uniform sampler2D u_normalmapTexture;
+#endif
+
+#if (DIRECTIONAL_LIGHT_COUNT > 0)
+uniform vec3 u_directionalLightColor[DIRECTIONAL_LIGHT_COUNT];
+#if !defined(BUMPED)
+uniform vec3 u_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+#endif
+
+#if (POINT_LIGHT_COUNT > 0)
+uniform vec3 u_pointLightColor[POINT_LIGHT_COUNT];
+uniform vec3 u_pointLightPosition[POINT_LIGHT_COUNT];
+uniform float u_pointLightRangeInverse[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+uniform vec3 u_spotLightColor[SPOT_LIGHT_COUNT];
+uniform float u_spotLightRangeInverse[SPOT_LIGHT_COUNT];
+uniform float u_spotLightInnerAngleCos[SPOT_LIGHT_COUNT];
+uniform float u_spotLightOuterAngleCos[SPOT_LIGHT_COUNT];
+#if !defined(BUMPED)
+uniform vec3 u_spotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+#endif
+
+#if defined(SPECULAR)
+uniform float u_specularExponent;
+#endif
+
+#endif
+
+#if defined(MODULATE_COLOR)
+uniform vec4 u_modulateColor;
+#endif
+
+#if defined(MODULATE_ALPHA)
+uniform float u_modulateAlpha;
+#endif
+
+///////////////////////////////////////////////////////////
+// Variables
+vec4 _baseColor;
+
+///////////////////////////////////////////////////////////
+// Varyings
+varying vec2 v_texCoord;
+
+#if defined(LIGHTMAP)
+varying vec2 v_texCoord1;
+#endif
+
+#if defined(LIGHTING)
+
+#if !defined(BUMPED)
+varying vec3 v_normalVector;
+#endif
+
+#if defined(BUMPED) && (DIRECTIONAL_LIGHT_COUNT > 0)
+varying vec3 v_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+
+#if (POINT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToPointLightDirection[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToSpotLightDirection[SPOT_LIGHT_COUNT];
+#if defined(BUMPED)
+varying vec3 v_spotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+#endif
+
+#if defined(SPECULAR)
+varying vec3 v_cameraDirection; 
+#endif
+
+#include "lighting.frag"
+
+#endif
+
+
+void main()
+{
+    _baseColor = texture2D(u_diffuseTexture, v_texCoord);
+ 
+    gl_FragColor.a = _baseColor.a;
+
+    #if defined(TEXTURE_DISCARD_ALPHA)
+    if (gl_FragColor.a < 0.5)
+        discard;
+    #endif
+
+    #if defined(LIGHTING)
+
+    gl_FragColor.rgb = getLitPixel();
+    #else
+    gl_FragColor.rgb = _baseColor.rgb;
+    #endif
+
+	#if defined(LIGHTMAP)
+	vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord1);
+	gl_FragColor.rgb *= lightColor.rgb;
+	#endif
+
+    #if defined(MODULATE_COLOR)
+    gl_FragColor *= u_modulateColor;
+    #endif
+
+    #if defined(MODULATE_ALPHA)
+    gl_FragColor.a *= u_modulateAlpha;
+    #endif
+}

+ 167 - 92
gameplay/res/shaders/textured.vert

@@ -1,92 +1,167 @@
-#define LIGHTING
-
-// Attributes
-attribute vec4 a_position;									// Vertex position							(x, y, z, w)
-attribute vec3 a_normal;									// Vertex normal							(x, y, z)
-attribute vec2 a_texCoord;									// Vertex texture coordinate				(u, v)
-#if defined(SKINNING)
-attribute vec4 a_blendWeights;								// Vertex blend weight, up to 4				(0, 1, 2, 3) 
-attribute vec4 a_blendIndices;								// Vertex blend index int u_matrixPalette	(0, 1, 2, 3)
-#endif
-
-// 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
-#if defined(SPECULAR) || defined(SPOT_LIGHT) || defined(POINT_LIGHT)
-uniform mat4 u_worldViewMatrix;								// Matrix to tranform a position to view space
-#endif
-#if defined(SKINNING)
-uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
-#endif
-#if defined(SPECULAR)
-uniform vec3 u_cameraPosition;                 				// Position of the camera in view space
-#endif
-#if defined(TEXTURE_REPEAT)
-uniform vec2 u_textureRepeat;								// Texture repeat for tiling
-#endif
-#if defined(TEXTURE_OFFSET)
-uniform vec2 u_textureOffset;								// Texture offset
-#endif
-#if defined(POINT_LIGHT)
-uniform vec3 u_pointLightPosition;							// Position of light
-uniform float u_pointLightRangeInverse;						// Inverse of light range 
-#elif defined(SPOT_LIGHT)
-uniform vec3 u_spotLightPosition;							// Position of light
-uniform float u_spotLightRangeInverse;						// Inverse of light range
-uniform vec3 u_spotLightDirection;                          // Direction of a spot light source
-#else
-#endif
-
-// Varyings
-varying vec3 v_normalVector;								// Normal vector in view space
-varying vec2 v_texCoord;									// Texture coordinate
-#if defined(SPECULAR)
-varying vec3 v_cameraDirection;								// Direction the camera is looking at in tangent space
-#endif
-#if defined(POINT_LIGHT)
-varying vec3 v_vertexToPointLightDirection;					// Direction of point light w.r.t current vertex in tangent space
-varying float v_pointLightAttenuation;						// Attenuation of point light
-#include "lighting-point.vert"
-#elif defined(SPOT_LIGHT)
-varying vec3 v_vertexToSpotLightDirection;					// Direction of the spot light w.r.t current vertex in tangent space
-varying float v_spotLightAttenuation;						// Attenuation of spot light
-
-// Lighting
-#include "lighting-spot.vert"
-#else
-#include "lighting-directional.vert"
-#endif
-
-// Skinning
-#if defined(SKINNING)
-#include "skinning.vert"
-#else
-#include "skinning-none.vert" 
-#endif
-
-
-void main()
-{
-    // Get the position and normal
-    vec4 position = getPosition();
-    vec3 normal = getNormal();
-
-    // Transform position to clip space.
-    gl_Position = u_worldViewProjectionMatrix * position;
-
-    // Transform normal to view space.
-	mat3 normalMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
-    v_normalVector = normalMatrix * normal;
-
-    // Apply light.
-    applyLight(position);
-
-    // Texture transformation
-    v_texCoord = a_texCoord;
-    #if defined(TEXTURE_REPEAT)
-    v_texCoord *= u_textureRepeat;
-    #endif
-    #if defined(TEXTURE_OFFSET)
-    v_texCoord += u_textureOffset;
-    #endif
-}
+#ifndef DIRECTIONAL_LIGHT_COUNT
+#define DIRECTIONAL_LIGHT_COUNT 0
+#endif
+#ifndef SPOT_LIGHT_COUNT
+#define SPOT_LIGHT_COUNT 0
+#endif
+#ifndef POINT_LIGHT_COUNT
+#define POINT_LIGHT_COUNT 0
+#endif
+#if (DIRECTIONAL_LIGHT_COUNT > 0) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+#define LIGHTING
+#endif
+
+///////////////////////////////////////////////////////////
+// Atributes
+attribute vec4 a_position;
+
+#if defined(SKINNING)
+attribute vec4 a_blendWeights;
+attribute vec4 a_blendIndices;
+#endif
+
+attribute vec2 a_texCoord;
+
+#if defined(LIGHTMAP)
+attribute vec2 a_texCoord1; 
+#endif
+
+#if defined(LIGHTING)
+attribute vec3 a_normal;
+
+#if defined(BUMPED)
+attribute vec3 a_tangent;
+attribute vec3 a_binormal;
+#endif
+
+#endif
+
+///////////////////////////////////////////////////////////
+// Uniforms
+uniform mat4 u_worldViewProjectionMatrix;
+#if defined(SKINNING)
+uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];
+#endif
+
+#if defined(LIGHTING)
+uniform mat4 u_inverseTransposeWorldViewMatrix;
+
+#if defined(SPECULAR) || (POINT_LIGHT_COUNT > 0) || (SPOT_LIGHT_COUNT > 0)
+uniform mat4 u_worldViewMatrix;
+#endif
+
+#if defined(BUMPED) && (DIRECTIONAL_LIGHT_COUNT > 0)
+uniform vec3 u_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+
+#if (POINT_LIGHT_COUNT > 0)
+uniform vec3 u_pointLightPosition[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0) 
+uniform vec3 u_spotLightPosition[SPOT_LIGHT_COUNT];
+#if defined(BUMPED)
+uniform vec3 u_spotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+#endif
+
+#if defined(SPECULAR)
+uniform vec3 u_cameraPosition;
+#endif
+
+#endif
+
+#if defined(TEXTURE_REPEAT)
+uniform vec2 u_textureRepeat;
+#endif
+
+#if defined(TEXTURE_OFFSET)
+uniform vec2 u_textureOffset;
+#endif
+
+///////////////////////////////////////////////////////////
+// Varyings
+varying vec2 v_texCoord;
+
+#if defined(LIGHTMAP)
+varying vec2 v_texCoord1;
+#endif
+
+#if defined(LIGHTING)
+
+#if !defined(BUMPED)
+varying vec3 v_normalVector;
+#endif
+
+#if defined(BUMPED) && (DIRECTIONAL_LIGHT_COUNT > 0)
+varying vec3 v_directionalLightDirection[DIRECTIONAL_LIGHT_COUNT];
+#endif
+
+#if (POINT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToPointLightDirection[POINT_LIGHT_COUNT];
+#endif
+
+#if (SPOT_LIGHT_COUNT > 0)
+varying vec3 v_vertexToSpotLightDirection[SPOT_LIGHT_COUNT];
+#if defined(BUMPED)
+varying vec3 v_spotLightDirection[SPOT_LIGHT_COUNT];
+#endif
+#endif
+
+#if defined(SPECULAR)
+varying vec3 v_cameraDirection;
+#endif
+
+#include "lighting.vert"
+
+#endif
+
+#if defined(SKINNING)
+#include "skinning.vert"
+#else
+#include "skinning-none.vert" 
+#endif
+
+void main()
+{
+    vec4 position = getPosition();
+    gl_Position = u_worldViewProjectionMatrix * position;
+
+    #if defined(LIGHTING)
+    vec3 normal = getNormal();
+    // Transform the normal, tangent and binormals to view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz, u_inverseTransposeWorldViewMatrix[1].xyz, u_inverseTransposeWorldViewMatrix[2].xyz);
+    vec3 normalVector = normalize(inverseTransposeWorldViewMatrix * normal);
+    
+    #if defined(BUMPED)
+    
+    vec3 tangent = getTangent();
+    vec3 binormal = getBinormal();
+    vec3 tangentVector  = normalize(inverseTransposeWorldViewMatrix * tangent);
+    vec3 binormalVector = normalize(inverseTransposeWorldViewMatrix * binormal);
+    mat3 tangentSpaceTransformMatrix = mat3(tangentVector.x, binormalVector.x, normalVector.x, tangentVector.y, binormalVector.y, normalVector.y, tangentVector.z, binormalVector.z, normalVector.z);
+    applyLight(position, tangentSpaceTransformMatrix);
+    
+    #else
+    
+    v_normalVector = normalVector;
+    applyLight(position);
+    
+    #endif
+    
+    #endif 
+    
+    v_texCoord = a_texCoord;
+    
+    #if defined(TEXTURE_REPEAT)
+    v_texCoord *= u_textureRepeat;
+    #endif
+    
+    #if defined(TEXTURE_OFFSET)
+    v_texCoord += u_textureOffset;
+    #endif
+    
+    #if defined(LIGHTMAP)
+    v_texCoord1 = a_texCoord1;
+    #endif
+}

BIN
samples/browser/res/common/baroque.gpb → gameplay/res/ui/arial.gpb


+ 1 - 1
gameplay/src/AIController.h

@@ -8,7 +8,7 @@ namespace gameplay
 {
 
 /**
- * The AIController facilitates state machine execution and message passing
+ * Defines and facilitates the state machine execution and message passing
  * between AI objects in the game. This class is generally not interfaced
  * with directly.
  */

+ 1 - 1
gameplay/src/AIMessage.h

@@ -5,7 +5,7 @@ namespace gameplay
 {
 
 /**
- * Defines a simple, flexible message structure used for passing messages through
+ * Defines a simple message structure used for passing messages through
  * the AI system.
  *
  * Messages can store an arbitrary number of parameters. For the sake of simplicity,

+ 1 - 3
gameplay/src/AIState.h

@@ -11,13 +11,11 @@ class AIAgent;
 class AIStateMachine;
 
 /**
- * Represents a single state in an AIStateMachine.
+ * Defines a single state in an AIStateMachine.
  *
  * An AIState encapsulates a state and unit of work within an AI
  * state machine. Events can be programmed or scripted when the
  * state is entered, exited and each frame/tick in its update event.
- *
- * 
  */
 class AIState : public Ref, public ScriptTarget
 {

+ 143 - 144
gameplay/src/AIStateMachine.cpp

@@ -1,144 +1,143 @@
-#include "Base.h"
-#include "AIStateMachine.h"
-#include "AIAgent.h"
-#include "AIMessage.h"
-#include "Game.h"
-
-namespace gameplay
-{
-
-AIStateMachine::AIStateMachine(AIAgent* agent)
-    : _agent(agent)
-{
-    GP_ASSERT(agent);
-    if (AIState::_empty)
-        AIState::_empty->addRef();
-    else
-        AIState::_empty = new AIState("");
-    _currentState = AIState::_empty;
-}
-
-AIStateMachine::~AIStateMachine()
-{
-    // Release all states
-    for (std::list<AIState*>::iterator itr = _states.begin(); itr != _states.end(); ++itr)
-    {
-        (*itr)->release();
-    }
-
-    if (AIState::_empty)
-    {
-        if (AIState::_empty->getRefCount() == 1)
-        {
-            SAFE_RELEASE(AIState::_empty);
-        }
-        else
-        {
-            AIState::_empty->release();
-        }
-    }
-}
-
-AIAgent* AIStateMachine::getAgent() const
-{
-    return _agent;
-}
-
-AIState* AIStateMachine::addState(const char* id)
-{
-    AIState* state = AIState::create(id);
-    _states.push_back(state);
-    return state;
-}
-
-void AIStateMachine::addState(AIState* state)
-{
-    state->addRef();
-    _states.push_back(state);
-}
-
-void AIStateMachine::removeState(AIState* state)
-{
-    std::list<AIState*>::iterator itr = std::find(_states.begin(), _states.end(), state);
-    if (itr != _states.end())
-    {
-        _states.erase(itr);
-        state->release();
-    }
-}
-
-AIState* AIStateMachine::getState(const char* id) const
-{
-    GP_ASSERT(id);
-
-    AIState* state;
-    for (std::list<AIState*>::const_iterator itr = _states.begin(); itr != _states.end(); ++itr)
-    {
-        state = (*itr);
-
-        if (strcmp(id, state->getId()) == 0)
-            return state;
-    }
-
-    return NULL;
-}
-
-AIState* AIStateMachine::getActiveState() const
-{
-    return _currentState;
-}
-
-bool AIStateMachine::hasState(AIState* state) const
-{
-    GP_ASSERT(state);
-
-    return (std::find(_states.begin(), _states.end(), state) != _states.end());
-}
-
-AIState* AIStateMachine::setState(const char* id)
-{
-    AIState* state = getState(id);
-    if (state)
-        sendChangeStateMessage(state);
-    return state;
-}
-
-bool AIStateMachine::setState(AIState* state)
-{
-    if (hasState(state))
-    {
-        sendChangeStateMessage(state);
-        return true;
-    }
-
-    return false;
-}
-
-void AIStateMachine::sendChangeStateMessage(AIState* newState)
-{
-    AIMessage* message = AIMessage::create(0, _agent->getId(), _agent->getId(), 1);
-    message->_messageType = AIMessage::MESSAGE_TYPE_STATE_CHANGE;
-    message->setString(0, newState->getId());
-    Game::getInstance()->getAIController()->sendMessage(message);
-}
-
-void AIStateMachine::setStateInternal(AIState* state)
-{
-    GP_ASSERT(hasState(state));
-
-    // Fire the exit event for the current state
-    _currentState->exit(this);
-
-    // Set the new state
-    _currentState = state;
-
-    // Fire the enter event for the new state
-    _currentState->enter(this);
-}
-
-void AIStateMachine::update(float elapsedTime)
-{
-    _currentState->update(this, elapsedTime);
-}
-
-}
+#include "Base.h"
+#include "AIStateMachine.h"
+#include "AIAgent.h"
+#include "AIMessage.h"
+#include "Game.h"
+
+namespace gameplay
+{
+
+AIStateMachine::AIStateMachine(AIAgent* agent)
+    : _agent(agent)
+{
+    GP_ASSERT(agent);
+    if (AIState::_empty)
+        AIState::_empty->addRef();
+    else
+        AIState::_empty = new AIState("");
+    _currentState = AIState::_empty;
+}
+
+AIStateMachine::~AIStateMachine()
+{
+    // Release all states
+    for (std::list<AIState*>::iterator itr = _states.begin(); itr != _states.end(); ++itr)
+    {
+        (*itr)->release();
+    }
+
+    if (AIState::_empty)
+    {
+        if (AIState::_empty->getRefCount() == 1)
+        {
+            SAFE_RELEASE(AIState::_empty);
+        }
+        else
+        {
+            AIState::_empty->release();
+        }
+    }
+}
+
+AIAgent* AIStateMachine::getAgent() const
+{
+    return _agent;
+}
+
+AIState* AIStateMachine::addState(const char* id)
+{
+    AIState* state = AIState::create(id);
+    _states.push_back(state);
+    return state;
+}
+
+void AIStateMachine::addState(AIState* state)
+{
+    state->addRef();
+    _states.push_back(state);
+}
+
+void AIStateMachine::removeState(AIState* state)
+{
+    std::list<AIState*>::iterator itr = std::find(_states.begin(), _states.end(), state);
+    if (itr != _states.end())
+    {
+        _states.erase(itr);
+        state->release();
+    }
+}
+
+AIState* AIStateMachine::getState(const char* id) const
+{
+    GP_ASSERT(id);
+
+    for (std::list<AIState*>::const_iterator itr = _states.begin(); itr != _states.end(); ++itr)
+    {
+        AIState* state = (*itr);
+
+        if (strcmp(id, state->getId()) == 0)
+            return state;
+    }
+
+    return NULL;
+}
+
+AIState* AIStateMachine::getActiveState() const
+{
+    return _currentState;
+}
+
+bool AIStateMachine::hasState(AIState* state) const
+{
+    GP_ASSERT(state);
+
+    return (std::find(_states.begin(), _states.end(), state) != _states.end());
+}
+
+AIState* AIStateMachine::setState(const char* id)
+{
+    AIState* state = getState(id);
+    if (state)
+        sendChangeStateMessage(state);
+    return state;
+}
+
+bool AIStateMachine::setState(AIState* state)
+{
+    if (hasState(state))
+    {
+        sendChangeStateMessage(state);
+        return true;
+    }
+
+    return false;
+}
+
+void AIStateMachine::sendChangeStateMessage(AIState* newState)
+{
+    AIMessage* message = AIMessage::create(0, _agent->getId(), _agent->getId(), 1);
+    message->_messageType = AIMessage::MESSAGE_TYPE_STATE_CHANGE;
+    message->setString(0, newState->getId());
+    Game::getInstance()->getAIController()->sendMessage(message);
+}
+
+void AIStateMachine::setStateInternal(AIState* state)
+{
+    GP_ASSERT(hasState(state));
+
+    // Fire the exit event for the current state
+    _currentState->exit(this);
+
+    // Set the new state
+    _currentState = state;
+
+    // Fire the enter event for the new state
+    _currentState->enter(this);
+}
+
+void AIStateMachine::update(float elapsedTime)
+{
+    _currentState->update(this, elapsedTime);
+}
+
+}

+ 57 - 54
gameplay/src/AbsoluteLayout.cpp

@@ -1,55 +1,58 @@
-#include "Base.h"
-#include "Control.h"
-#include "AbsoluteLayout.h"
-#include "Container.h"
-
-namespace gameplay
-{
-
-static AbsoluteLayout* __instance;
-
-AbsoluteLayout::AbsoluteLayout()
-{
-}
-
-AbsoluteLayout::~AbsoluteLayout()
-{
-    __instance = NULL;
-}
-
-AbsoluteLayout* AbsoluteLayout::create()
-{
-    if (!__instance)
-    {
-        __instance = new AbsoluteLayout();
-    }
-    else
-    {
-        __instance->addRef();
-    }
-
-    return __instance;
-}
-
-Layout::Type AbsoluteLayout::getType()
-{
-    return Layout::LAYOUT_ABSOLUTE;
-}
-
-void AbsoluteLayout::update(const Container* container, const Vector2& offset)
-{
-    GP_ASSERT(container);
-
-    // An AbsoluteLayout does nothing to modify the layout of Controls.
-    const std::vector<Control*>& controls = container->getControls();
-    for (size_t i = 0, count = controls.size(); i < count; i++)
-    {
-        Control* control = controls[i];
-        GP_ASSERT(control);
-
-        align(control, container);
-        control->update(container, offset);
-    }
-}
-
+#include "Base.h"
+#include "Control.h"
+#include "AbsoluteLayout.h"
+#include "Container.h"
+
+namespace gameplay
+{
+
+static AbsoluteLayout* __instance;
+
+AbsoluteLayout::AbsoluteLayout()
+{
+}
+
+AbsoluteLayout::~AbsoluteLayout()
+{
+    __instance = NULL;
+}
+
+AbsoluteLayout* AbsoluteLayout::create()
+{
+    if (!__instance)
+    {
+        __instance = new AbsoluteLayout();
+    }
+    else
+    {
+        __instance->addRef();
+    }
+
+    return __instance;
+}
+
+Layout::Type AbsoluteLayout::getType()
+{
+    return Layout::LAYOUT_ABSOLUTE;
+}
+
+void AbsoluteLayout::update(const Container* container, const Vector2& offset)
+{
+    GP_ASSERT(container);
+
+    // An AbsoluteLayout does nothing to modify the layout of Controls.
+    const std::vector<Control*>& controls = container->getControls();
+    for (size_t i = 0, count = controls.size(); i < count; i++)
+    {
+        Control* control = controls[i];
+        GP_ASSERT(control);
+
+        if (control->isVisible())
+        {
+            align(control, container);
+            control->update(container, offset);
+        }
+    }
+}
+
 }

+ 67 - 67
gameplay/src/AbsoluteLayout.h

@@ -1,68 +1,68 @@
-#ifndef ABSOLUTELAYOUT_H_
-#define ABSOLUTELAYOUT_H_
-
-#include "Layout.h"
-
-namespace gameplay
-{
-
-/**
- * Defines a Layout for forms and containers that requires the user
- * to specify absolute positions for all contained controls.
- */
-class AbsoluteLayout : public Layout
-{
-    friend class Form;
-    friend class Container;
-
-public:
-
-    /**
-     * Get the type of this Layout.
-     *
-     * @return Layout::LAYOUT_ABSOLUTE
-     */
-    Layout::Type getType();
-
-protected:
-
-    /**
-     * Update the controls contained by the specified container.
-     *
-     * An AbsoluteLayout does nothing to modify the layout of controls.
-     * It simply calls update() on any control that is dirty.
-     *
-     * @param container The container to update.
-     * @param offset The layout offset.
-     */
-    void update(const Container* container, const Vector2& offset);
-
-private:
-    
-    /*
-     * Constructor.
-     */
-    AbsoluteLayout();
-    
-    /*
-     * Constructor.
-     */
-    AbsoluteLayout(const AbsoluteLayout& copy);
-    
-    /*
-     * Destructor.
-     */
-    virtual ~AbsoluteLayout();
-
-    /**
-     * Create an AbsoluteLayout.
-     *
-     * @return An AbsoluteLayout object.
-     */
-    static AbsoluteLayout* create();
-
-};
-
-}
-
+#ifndef ABSOLUTELAYOUT_H_
+#define ABSOLUTELAYOUT_H_
+
+#include "Layout.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a Layout for forms and containers that requires the user
+ * to specify absolute positions for all contained controls.
+ */
+class AbsoluteLayout : public Layout
+{
+    friend class Form;
+    friend class Container;
+
+public:
+
+    /**
+     * Get the type of this Layout.
+     *
+     * @return Layout::LAYOUT_ABSOLUTE
+     */
+    Layout::Type getType();
+
+protected:
+
+    /**
+     * Update the controls contained by the specified container.
+     *
+     * An AbsoluteLayout does nothing to modify the layout of controls.
+     * It simply calls update() on any control that is dirty.
+     *
+     * @param container The container to update.
+     * @param offset The layout offset.
+     */
+    void update(const Container* container, const Vector2& offset);
+
+private:
+    
+    /*
+     * Constructor.
+     */
+    AbsoluteLayout();
+    
+    /*
+     * Constructor.
+     */
+    AbsoluteLayout(const AbsoluteLayout& copy);
+    
+    /*
+     * Destructor.
+     */
+    virtual ~AbsoluteLayout();
+
+    /**
+     * Create an AbsoluteLayout.
+     *
+     * @return An AbsoluteLayout object.
+     */
+    static AbsoluteLayout* create();
+
+};
+
+}
+
 #endif

+ 476 - 477
gameplay/src/Animation.cpp

@@ -1,477 +1,476 @@
-#include "Base.h"
-#include "Animation.h"
-#include "AnimationController.h"
-#include "AnimationClip.h"
-#include "AnimationTarget.h"
-#include "Game.h"
-#include "Transform.h"
-#include "Properties.h"
-
-#define ANIMATION_INDEFINITE_STR "INDEFINITE"
-#define ANIMATION_DEFAULT_CLIP 0
-#define ANIMATION_ROTATE_OFFSET 0
-#define ANIMATION_SRT_OFFSET 3
-
-namespace gameplay
-{
-
-Animation::Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type)
-    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0L), _defaultClip(NULL), _clips(NULL)
-{
-    createChannel(target, propertyId, keyCount, keyTimes, keyValues, type);
-
-    // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
-    release();
-    GP_ASSERT(getRefCount() == 1);
-}
-
-Animation::Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
-    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0L), _defaultClip(NULL), _clips(NULL)
-{
-    createChannel(target, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
-    // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
-    release();
-    GP_ASSERT(getRefCount() == 1);
-}
-
-Animation::Animation(const char* id)
-    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0L), _defaultClip(NULL), _clips(NULL)
-{
-}
-
-Animation::~Animation()
-{
-    _channels.clear();
-
-    if (_defaultClip)
-    {
-        if (_defaultClip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
-        {
-            GP_ASSERT(_controller);
-            _controller->unschedule(_defaultClip);
-        }
-        SAFE_RELEASE(_defaultClip);
-    }
-
-    if (_clips)
-    {
-        std::vector<AnimationClip*>::iterator clipIter = _clips->begin();
-
-        while (clipIter != _clips->end())
-        {
-            AnimationClip* clip = *clipIter;
-            GP_ASSERT(clip);
-            if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
-            {
-                GP_ASSERT(_controller);
-                _controller->unschedule(clip);
-            }
-            SAFE_RELEASE(clip);
-            clipIter++;
-        }
-        _clips->clear();
-    }
-    SAFE_DELETE(_clips);
-}
-
-Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration)
-    : _animation(animation), _target(target), _propertyId(propertyId), _curve(curve), _duration(duration)
-{
-    GP_ASSERT(_animation);
-    GP_ASSERT(_target);
-    GP_ASSERT(_curve);
-
-    // get property component count, and ensure the property exists on the AnimationTarget by getting the property component count.
-    GP_ASSERT(_target->getAnimationPropertyComponentCount(propertyId));
-    _curve->addRef();
-    _target->addChannel(this);
-    _animation->addRef();
-}
-
-Animation::Channel::Channel(const Channel& copy, Animation* animation, AnimationTarget* target)
-    : _animation(animation), _target(target), _propertyId(copy._propertyId), _curve(copy._curve), _duration(copy._duration)
-{
-    GP_ASSERT(_curve);
-    GP_ASSERT(_target);
-    GP_ASSERT(_animation);
-
-    _curve->addRef();
-    _target->addChannel(this);
-    _animation->addRef();
-}
-
-Animation::Channel::~Channel()
-{
-    SAFE_RELEASE(_curve);
-    SAFE_RELEASE(_animation);
-}
-
-Curve* Animation::Channel::getCurve() const
-{
-    return _curve;
-}
-
-const char* Animation::getId() const
-{
-    return _id.c_str();
-}
-
-unsigned long Animation::getDuration() const
-{
-    return _duration;
-}
-
-void Animation::createClips(const char* url)
-{
-    Properties* properties = Properties::create(url);
-    GP_ASSERT(properties);
-
-    Properties* pAnimation = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
-    GP_ASSERT(pAnimation);
-
-    int frameCount = pAnimation->getInt("frameCount");
-    if (frameCount <= 0)
-        GP_ERROR("The animation's frame count must be greater than 0.");
-
-    createClips(pAnimation, (unsigned int)frameCount);
-
-    SAFE_DELETE(properties);
-}
-
-AnimationClip* Animation::createClip(const char* id, unsigned long start, unsigned long end)
-{
-    AnimationClip* clip = new AnimationClip(id, this, start, end);
-    addClip(clip);
-    return clip;
-}
-
-AnimationClip* Animation::getClip(const char* id)
-{
-    // If id is NULL return the default clip.
-    if (id == NULL)
-    {
-        if (_defaultClip == NULL)
-            createDefaultClip();
-
-        return _defaultClip;
-    }
-    else
-    {
-        return findClip(id);
-    }
-}
-
-AnimationClip* Animation::getClip(unsigned int index) const
-{
-    if (_clips)
-        return _clips->at(index);
-
-    return NULL;
-}
-
-unsigned int Animation::getClipCount() const
-{
-    return _clips ? (unsigned int)_clips->size() : 0;
-}
-
-void Animation::play(const char* clipId)
-{
-    // If id is NULL, play the default clip.
-    if (clipId == NULL)
-    {
-        if (_defaultClip == NULL)
-            createDefaultClip();
-
-        _defaultClip->play();
-    }
-    else
-    {
-        // Find animation clip and play.
-        AnimationClip* clip = findClip(clipId);
-        if (clip != NULL)
-            clip->play();
-    }
-}
-
-void Animation::stop(const char* clipId)
-{
-    // If id is NULL, play the default clip.
-    if (clipId == NULL)
-    {
-        if (_defaultClip)
-            _defaultClip->stop();
-    }
-    else
-    {
-        // Find animation clip and play.
-        AnimationClip* clip = findClip(clipId);
-        if (clip != NULL)
-            clip->stop();
-    }
-}
-
-void Animation::pause(const char * clipId)
-{
-    if (clipId == NULL)
-    {
-        if (_defaultClip)
-            _defaultClip->pause();
-    }
-    else
-    {
-        AnimationClip* clip = findClip(clipId);
-        if (clip != NULL)
-            clip->pause();
-    }
-}
-
-bool Animation::targets(AnimationTarget* target) const
-{
-    for (std::vector<Animation::Channel*>::const_iterator itr = _channels.begin(); itr != _channels.end(); ++itr)
-    {
-        GP_ASSERT(*itr);
-        if ((*itr)->_target == target)
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-void Animation::createDefaultClip()
-{
-    _defaultClip = new AnimationClip("default_clip", this, 0.0f, _duration);
-}
-
-void Animation::createClips(Properties* animationProperties, unsigned int frameCount)
-{
-    GP_ASSERT(animationProperties);
-
-    Properties* pClip = animationProperties->getNextNamespace();
-
-    while (pClip != NULL && std::strcmp(pClip->getNamespace(), "clip") == 0)
-    {
-        int begin = pClip->getInt("begin");
-        int end = pClip->getInt("end");
-
-        AnimationClip* clip = createClip(pClip->getId(), ((float)begin / frameCount) * _duration, ((float)end / frameCount) * _duration);
-
-        const char* repeat = pClip->getString("repeatCount");
-        if (repeat)
-        {
-            if (strcmp(repeat, ANIMATION_INDEFINITE_STR) == 0)
-            {
-                clip->setRepeatCount(AnimationClip::REPEAT_INDEFINITE);
-            }
-            else
-            {
-                float value;
-                sscanf(repeat, "%f", &value);
-                clip->setRepeatCount(value);
-            }
-        }
-
-        const char* speed = pClip->getString("speed");
-        if (speed)
-        {
-            float value;
-            sscanf(speed, "%f", &value);
-            clip->setSpeed(value);
-        }
-
-        clip->setLoopBlendTime(pClip->getFloat("loopBlendTime")); // returns zero if not specified
-
-        pClip = animationProperties->getNextNamespace();
-    }
-}
-
-void Animation::addClip(AnimationClip* clip)
-{
-    if (_clips == NULL)
-        _clips = new std::vector<AnimationClip*>;
-
-    GP_ASSERT(clip);
-    _clips->push_back(clip);
-}
-
-AnimationClip* Animation::findClip(const char* id) const
-{
-    if (_clips)
-    {
-        AnimationClip* clip = NULL;
-        size_t clipCount = _clips->size();
-        for (size_t i = 0; i < clipCount; i++)
-        {
-            clip = _clips->at(i);
-            GP_ASSERT(clip);
-            if (clip->_id.compare(id) == 0)
-            {
-                return clip;
-            }
-        }
-    }
-    return NULL;
-}
-
-Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type)
-{
-    GP_ASSERT(target);
-    GP_ASSERT(keyTimes);
-    GP_ASSERT(keyValues);
-
-    unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
-    GP_ASSERT(propertyComponentCount > 0);
-
-    Curve* curve = Curve::create(keyCount, propertyComponentCount);
-    GP_ASSERT(curve);
-    if (target->_targetType == AnimationTarget::TRANSFORM)
-        setTransformRotationOffset(curve, propertyId);
-
-    unsigned int lowest = keyTimes[0];
-    unsigned long duration = keyTimes[keyCount-1] - lowest;
-
-    float* normalizedKeyTimes = new float[keyCount];
-
-    normalizedKeyTimes[0] = 0.0f;
-    curve->setPoint(0, normalizedKeyTimes[0], keyValues, (Curve::InterpolationType) type);
-
-    unsigned int pointOffset = propertyComponentCount;
-    unsigned int i = 1;
-    for (; i < keyCount - 1; i++)
-    {
-        normalizedKeyTimes[i] = (float) (keyTimes[i] - lowest) / (float) duration;
-        curve->setPoint(i, normalizedKeyTimes[i], (keyValues + pointOffset), (Curve::InterpolationType) type);
-        pointOffset += propertyComponentCount;
-    }
-    if (keyCount > 1) {
-        i = keyCount - 1;
-        normalizedKeyTimes[i] = 1.0f;
-        curve->setPoint(i, normalizedKeyTimes[i], keyValues + pointOffset, (Curve::InterpolationType) type);
-    }
-
-    SAFE_DELETE_ARRAY(normalizedKeyTimes);
-
-    Channel* channel = new Channel(this, target, propertyId, curve, duration);
-    curve->release();
-    addChannel(channel);
-    return channel;
-}
-
-Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
-{
-    GP_ASSERT(target);
-    GP_ASSERT(keyTimes);
-    GP_ASSERT(keyValues);
-
-    unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
-    GP_ASSERT(propertyComponentCount > 0);
-
-    Curve* curve = Curve::create(keyCount, propertyComponentCount);
-    GP_ASSERT(curve);
-    if (target->_targetType == AnimationTarget::TRANSFORM)
-        setTransformRotationOffset(curve, propertyId);
-
-    unsigned long lowest = keyTimes[0];
-    unsigned long duration = keyTimes[keyCount-1] - lowest;
-
-    float* normalizedKeyTimes = new float[keyCount];
-
-    normalizedKeyTimes[0] = 0.0f;
-    curve->setPoint(0, normalizedKeyTimes[0], keyValues, (Curve::InterpolationType) type, keyInValue, keyOutValue);
-
-    unsigned int pointOffset = propertyComponentCount;
-    unsigned int i = 1;
-    for (; i < keyCount - 1; i++)
-    {
-        normalizedKeyTimes[i] = (float) (keyTimes[i] - lowest) / (float) duration;
-        curve->setPoint(i, normalizedKeyTimes[i], (keyValues + pointOffset), (Curve::InterpolationType) type, (keyInValue + pointOffset), (keyOutValue + pointOffset));
-        pointOffset += propertyComponentCount;
-    }
-    i = keyCount - 1;
-    normalizedKeyTimes[i] = 1.0f;
-    curve->setPoint(i, normalizedKeyTimes[i], keyValues + pointOffset, (Curve::InterpolationType) type, keyInValue + pointOffset, keyOutValue + pointOffset);
-
-    SAFE_DELETE_ARRAY(normalizedKeyTimes);
-
-    Channel* channel = new Channel(this, target, propertyId, curve, duration);
-    curve->release();
-    addChannel(channel);
-    return channel;
-}
-
-void Animation::addChannel(Channel* channel)
-{
-    GP_ASSERT(channel);
-    _channels.push_back(channel);
-
-    if (channel->_duration > _duration)
-        _duration = channel->_duration;
-}
-
-void Animation::removeChannel(Channel* channel)
-{
-    std::vector<Animation::Channel*>::iterator itr = _channels.begin();
-    while (itr != _channels.end())
-    {
-        Animation::Channel* chan = *itr;
-        if (channel == chan)
-        {
-            _channels.erase(itr);
-            return;
-        }
-        else
-        {
-            itr++;
-        }
-    }
-}
-
-void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId)
-{
-    GP_ASSERT(curve);
-
-    switch (propertyId)
-    {
-    case Transform::ANIMATE_ROTATE:
-    case Transform::ANIMATE_ROTATE_TRANSLATE:
-        curve->setQuaternionOffset(ANIMATION_ROTATE_OFFSET);
-        return;
-    case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
-        curve->setQuaternionOffset(ANIMATION_SRT_OFFSET);
-        return;
-    }
-
-    return;
-}
-
-Animation* Animation::clone(Channel* channel, AnimationTarget* target)
-{
-    GP_ASSERT(channel);
-
-    Animation* animation = new Animation(getId());
-
-    Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
-    animation->addChannel(channelCopy);
-    // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
-    animation->release();
-    GP_ASSERT(animation->getRefCount() == 1);
-
-    // Clone the clips
-    
-    if (_defaultClip)
-    {
-        animation->_defaultClip = _defaultClip->clone(animation);
-    }
-    
-    if (_clips)
-    {
-        for (std::vector<AnimationClip*>::iterator it = _clips->begin(); it != _clips->end(); ++it)
-        {
-            AnimationClip* newClip = (*it)->clone(animation);
-            animation->addClip(newClip);
-        }
-    }
-    return animation;
-}
-
-}
+#include "Base.h"
+#include "Animation.h"
+#include "AnimationController.h"
+#include "AnimationClip.h"
+#include "AnimationTarget.h"
+#include "Game.h"
+#include "Transform.h"
+#include "Properties.h"
+
+#define ANIMATION_INDEFINITE_STR "INDEFINITE"
+#define ANIMATION_DEFAULT_CLIP 0
+#define ANIMATION_ROTATE_OFFSET 0
+#define ANIMATION_SRT_OFFSET 3
+
+namespace gameplay
+{
+
+Animation::Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type)
+    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0L), _defaultClip(NULL), _clips(NULL)
+{
+    createChannel(target, propertyId, keyCount, keyTimes, keyValues, type);
+
+    // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
+    release();
+    GP_ASSERT(getRefCount() == 1);
+}
+
+Animation::Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
+    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0L), _defaultClip(NULL), _clips(NULL)
+{
+    createChannel(target, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
+    // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
+    release();
+    GP_ASSERT(getRefCount() == 1);
+}
+
+Animation::Animation(const char* id)
+    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0L), _defaultClip(NULL), _clips(NULL)
+{
+}
+
+Animation::~Animation()
+{
+    _channels.clear();
+
+    if (_defaultClip)
+    {
+        if (_defaultClip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
+        {
+            GP_ASSERT(_controller);
+            _controller->unschedule(_defaultClip);
+        }
+        SAFE_RELEASE(_defaultClip);
+    }
+
+    if (_clips)
+    {
+        std::vector<AnimationClip*>::iterator clipIter = _clips->begin();
+
+        while (clipIter != _clips->end())
+        {
+            AnimationClip* clip = *clipIter;
+            GP_ASSERT(clip);
+            if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
+            {
+                GP_ASSERT(_controller);
+                _controller->unschedule(clip);
+            }
+            SAFE_RELEASE(clip);
+            clipIter++;
+        }
+        _clips->clear();
+    }
+    SAFE_DELETE(_clips);
+}
+
+Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration)
+    : _animation(animation), _target(target), _propertyId(propertyId), _curve(curve), _duration(duration)
+{
+    GP_ASSERT(_animation);
+    GP_ASSERT(_target);
+    GP_ASSERT(_curve);
+
+    // get property component count, and ensure the property exists on the AnimationTarget by getting the property component count.
+    GP_ASSERT(_target->getAnimationPropertyComponentCount(propertyId));
+    _curve->addRef();
+    _target->addChannel(this);
+    _animation->addRef();
+}
+
+Animation::Channel::Channel(const Channel& copy, Animation* animation, AnimationTarget* target)
+    : _animation(animation), _target(target), _propertyId(copy._propertyId), _curve(copy._curve), _duration(copy._duration)
+{
+    GP_ASSERT(_curve);
+    GP_ASSERT(_target);
+    GP_ASSERT(_animation);
+
+    _curve->addRef();
+    _target->addChannel(this);
+    _animation->addRef();
+}
+
+Animation::Channel::~Channel()
+{
+    SAFE_RELEASE(_curve);
+    SAFE_RELEASE(_animation);
+}
+
+Curve* Animation::Channel::getCurve() const
+{
+    return _curve;
+}
+
+const char* Animation::getId() const
+{
+    return _id.c_str();
+}
+
+unsigned long Animation::getDuration() const
+{
+    return _duration;
+}
+
+void Animation::createClips(const char* url)
+{
+    Properties* properties = Properties::create(url);
+    GP_ASSERT(properties);
+
+    Properties* pAnimation = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
+    GP_ASSERT(pAnimation);
+
+    int frameCount = pAnimation->getInt("frameCount");
+    if (frameCount <= 0)
+        GP_ERROR("The animation's frame count must be greater than 0.");
+
+    createClips(pAnimation, (unsigned int)frameCount);
+
+    SAFE_DELETE(properties);
+}
+
+AnimationClip* Animation::createClip(const char* id, unsigned long start, unsigned long end)
+{
+    AnimationClip* clip = new AnimationClip(id, this, start, end);
+    addClip(clip);
+    return clip;
+}
+
+AnimationClip* Animation::getClip(const char* id)
+{
+    // If id is NULL return the default clip.
+    if (id == NULL)
+    {
+        if (_defaultClip == NULL)
+            createDefaultClip();
+
+        return _defaultClip;
+    }
+    else
+    {
+        return findClip(id);
+    }
+}
+
+AnimationClip* Animation::getClip(unsigned int index) const
+{
+    if (_clips)
+        return _clips->at(index);
+
+    return NULL;
+}
+
+unsigned int Animation::getClipCount() const
+{
+    return _clips ? (unsigned int)_clips->size() : 0;
+}
+
+void Animation::play(const char* clipId)
+{
+    // If id is NULL, play the default clip.
+    if (clipId == NULL)
+    {
+        if (_defaultClip == NULL)
+            createDefaultClip();
+
+        _defaultClip->play();
+    }
+    else
+    {
+        // Find animation clip and play.
+        AnimationClip* clip = findClip(clipId);
+        if (clip != NULL)
+            clip->play();
+    }
+}
+
+void Animation::stop(const char* clipId)
+{
+    // If id is NULL, play the default clip.
+    if (clipId == NULL)
+    {
+        if (_defaultClip)
+            _defaultClip->stop();
+    }
+    else
+    {
+        // Find animation clip and play.
+        AnimationClip* clip = findClip(clipId);
+        if (clip != NULL)
+            clip->stop();
+    }
+}
+
+void Animation::pause(const char * clipId)
+{
+    if (clipId == NULL)
+    {
+        if (_defaultClip)
+            _defaultClip->pause();
+    }
+    else
+    {
+        AnimationClip* clip = findClip(clipId);
+        if (clip != NULL)
+            clip->pause();
+    }
+}
+
+bool Animation::targets(AnimationTarget* target) const
+{
+    for (std::vector<Animation::Channel*>::const_iterator itr = _channels.begin(); itr != _channels.end(); ++itr)
+    {
+        GP_ASSERT(*itr);
+        if ((*itr)->_target == target)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+void Animation::createDefaultClip()
+{
+    _defaultClip = new AnimationClip("default_clip", this, 0.0f, _duration);
+}
+
+void Animation::createClips(Properties* animationProperties, unsigned int frameCount)
+{
+    GP_ASSERT(animationProperties);
+
+    Properties* pClip = animationProperties->getNextNamespace();
+
+    while (pClip != NULL && std::strcmp(pClip->getNamespace(), "clip") == 0)
+    {
+        int begin = pClip->getInt("begin");
+        int end = pClip->getInt("end");
+
+        AnimationClip* clip = createClip(pClip->getId(), ((float)begin / frameCount) * _duration, ((float)end / frameCount) * _duration);
+
+        const char* repeat = pClip->getString("repeatCount");
+        if (repeat)
+        {
+            if (strcmp(repeat, ANIMATION_INDEFINITE_STR) == 0)
+            {
+                clip->setRepeatCount(AnimationClip::REPEAT_INDEFINITE);
+            }
+            else
+            {
+                float value;
+                sscanf(repeat, "%f", &value);
+                clip->setRepeatCount(value);
+            }
+        }
+
+        const char* speed = pClip->getString("speed");
+        if (speed)
+        {
+            float value;
+            sscanf(speed, "%f", &value);
+            clip->setSpeed(value);
+        }
+
+        clip->setLoopBlendTime(pClip->getFloat("loopBlendTime")); // returns zero if not specified
+
+        pClip = animationProperties->getNextNamespace();
+    }
+}
+
+void Animation::addClip(AnimationClip* clip)
+{
+    if (_clips == NULL)
+        _clips = new std::vector<AnimationClip*>;
+
+    GP_ASSERT(clip);
+    _clips->push_back(clip);
+}
+
+AnimationClip* Animation::findClip(const char* id) const
+{
+    if (_clips)
+    {
+        size_t clipCount = _clips->size();
+        for (size_t i = 0; i < clipCount; i++)
+        {
+            AnimationClip* clip = _clips->at(i);
+            GP_ASSERT(clip);
+            if (clip->_id.compare(id) == 0)
+            {
+                return clip;
+            }
+        }
+    }
+    return NULL;
+}
+
+Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type)
+{
+    GP_ASSERT(target);
+    GP_ASSERT(keyTimes);
+    GP_ASSERT(keyValues);
+
+    unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
+    GP_ASSERT(propertyComponentCount > 0);
+
+    Curve* curve = Curve::create(keyCount, propertyComponentCount);
+    GP_ASSERT(curve);
+    if (target->_targetType == AnimationTarget::TRANSFORM)
+        setTransformRotationOffset(curve, propertyId);
+
+    unsigned int lowest = keyTimes[0];
+    unsigned long duration = keyTimes[keyCount-1] - lowest;
+
+    float* normalizedKeyTimes = new float[keyCount];
+
+    normalizedKeyTimes[0] = 0.0f;
+    curve->setPoint(0, normalizedKeyTimes[0], keyValues, (Curve::InterpolationType) type);
+
+    unsigned int pointOffset = propertyComponentCount;
+    unsigned int i = 1;
+    for (; i < keyCount - 1; i++)
+    {
+        normalizedKeyTimes[i] = (float) (keyTimes[i] - lowest) / (float) duration;
+        curve->setPoint(i, normalizedKeyTimes[i], (keyValues + pointOffset), (Curve::InterpolationType) type);
+        pointOffset += propertyComponentCount;
+    }
+    if (keyCount > 1) {
+        i = keyCount - 1;
+        normalizedKeyTimes[i] = 1.0f;
+        curve->setPoint(i, normalizedKeyTimes[i], keyValues + pointOffset, (Curve::InterpolationType) type);
+    }
+
+    SAFE_DELETE_ARRAY(normalizedKeyTimes);
+
+    Channel* channel = new Channel(this, target, propertyId, curve, duration);
+    curve->release();
+    addChannel(channel);
+    return channel;
+}
+
+Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
+{
+    GP_ASSERT(target);
+    GP_ASSERT(keyTimes);
+    GP_ASSERT(keyValues);
+
+    unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
+    GP_ASSERT(propertyComponentCount > 0);
+
+    Curve* curve = Curve::create(keyCount, propertyComponentCount);
+    GP_ASSERT(curve);
+    if (target->_targetType == AnimationTarget::TRANSFORM)
+        setTransformRotationOffset(curve, propertyId);
+
+    unsigned long lowest = keyTimes[0];
+    unsigned long duration = keyTimes[keyCount-1] - lowest;
+
+    float* normalizedKeyTimes = new float[keyCount];
+
+    normalizedKeyTimes[0] = 0.0f;
+    curve->setPoint(0, normalizedKeyTimes[0], keyValues, (Curve::InterpolationType) type, keyInValue, keyOutValue);
+
+    unsigned int pointOffset = propertyComponentCount;
+    unsigned int i = 1;
+    for (; i < keyCount - 1; i++)
+    {
+        normalizedKeyTimes[i] = (float) (keyTimes[i] - lowest) / (float) duration;
+        curve->setPoint(i, normalizedKeyTimes[i], (keyValues + pointOffset), (Curve::InterpolationType) type, (keyInValue + pointOffset), (keyOutValue + pointOffset));
+        pointOffset += propertyComponentCount;
+    }
+    i = keyCount - 1;
+    normalizedKeyTimes[i] = 1.0f;
+    curve->setPoint(i, normalizedKeyTimes[i], keyValues + pointOffset, (Curve::InterpolationType) type, keyInValue + pointOffset, keyOutValue + pointOffset);
+
+    SAFE_DELETE_ARRAY(normalizedKeyTimes);
+
+    Channel* channel = new Channel(this, target, propertyId, curve, duration);
+    curve->release();
+    addChannel(channel);
+    return channel;
+}
+
+void Animation::addChannel(Channel* channel)
+{
+    GP_ASSERT(channel);
+    _channels.push_back(channel);
+
+    if (channel->_duration > _duration)
+        _duration = channel->_duration;
+}
+
+void Animation::removeChannel(Channel* channel)
+{
+    std::vector<Animation::Channel*>::iterator itr = _channels.begin();
+    while (itr != _channels.end())
+    {
+        Animation::Channel* chan = *itr;
+        if (channel == chan)
+        {
+            _channels.erase(itr);
+            return;
+        }
+        else
+        {
+            itr++;
+        }
+    }
+}
+
+void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId)
+{
+    GP_ASSERT(curve);
+
+    switch (propertyId)
+    {
+    case Transform::ANIMATE_ROTATE:
+    case Transform::ANIMATE_ROTATE_TRANSLATE:
+        curve->setQuaternionOffset(ANIMATION_ROTATE_OFFSET);
+        return;
+    case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
+        curve->setQuaternionOffset(ANIMATION_SRT_OFFSET);
+        return;
+    }
+
+    return;
+}
+
+Animation* Animation::clone(Channel* channel, AnimationTarget* target)
+{
+    GP_ASSERT(channel);
+
+    Animation* animation = new Animation(getId());
+
+    Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
+    animation->addChannel(channelCopy);
+    // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
+    animation->release();
+    GP_ASSERT(animation->getRefCount() == 1);
+
+    // Clone the clips
+    
+    if (_defaultClip)
+    {
+        animation->_defaultClip = _defaultClip->clone(animation);
+    }
+    
+    if (_clips)
+    {
+        for (std::vector<AnimationClip*>::iterator it = _clips->begin(); it != _clips->end(); ++it)
+        {
+            AnimationClip* newClip = (*it)->clone(animation);
+            animation->addClip(newClip);
+        }
+    }
+    return animation;
+}
+
+}

+ 241 - 239
gameplay/src/Animation.h

@@ -1,239 +1,241 @@
-#ifndef ANIMATION_H_
-#define ANIMATION_H_
-
-#include "Ref.h"
-#include "Properties.h"
-#include "Curve.h"
-
-namespace gameplay
-{
-
-class AnimationTarget;
-class AnimationController;
-class AnimationClip;
-
-/**
- * Defines a generic property animation.
- *
- * To run an animation you must play an AnimationClip.
- * Every Animation has the default clip which will run from begin-end time.
- * You can create additional clips to run only parts of an animation and control
- * various runtime characteristics, such as repeat count, etc.
- */
-class Animation : public Ref
-{
-    friend class AnimationClip;
-    friend class AnimationTarget;
-    friend class Bundle;
-
-public:
-
-    /**
-     * Gets the Animation's ID.
-     *
-     * @return The Animation's ID.
-     */
-    const char* getId() const;
-
-    /**
-     * Gets the Animation's duration.
-     *
-     * @return The Animation's duration (in milliseconds).
-     */
-    unsigned long getDuration() const;
-
-    /**
-     * Creates an AnimationClip from the Properties object defined at the specified URL,
-     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
-     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
-     *
-     * @param url The URL pointing to the Properties object containing the clip definitions.
-     */
-    void createClips(const char* url);
-
-    /**
-     * Creates an AnimationClip from the Animation.
-     *
-     * @param id The ID to the give the AnimationClip.
-     * @param start The time (in milliseconds) that the AnimationClip will begin from.
-     * @param end The time (in milliseconds) that the AnimationClip will end.
-     *
-     * @return The newly created AnimationClip; NULL if an AnimationClip already exists with the same ID.
-     * @script{create}
-     */
-    AnimationClip* createClip(const char* id, unsigned long start, unsigned long end);
-
-    /**
-     * Finds the AnimationClip with the specified name. If NULL, gets the default clip.
-     *
-     * @param clipId The ID of the AnimationClip to get.
-     *
-     * @return The AnimationClip with the specified ID; NULL if an AnimationClip with the given ID is not found.
-     */
-    AnimationClip* getClip(const char* clipId = NULL);
-
-    /**
-     * Returns the AnimationClip at the given index.
-     *
-     * @param index Index of the clip to return.
-     */
-    AnimationClip* getClip(unsigned int index) const;
-
-    /**
-     * Returns the number of animation clips in this animation.
-     */
-    unsigned int getClipCount() const;
-
-    /**
-     * Plays the AnimationClip with the specified name.
-     *
-     * @param clipId The ID of the AnimationClip to play. If NULL, plays the default clip.
-     */
-    void play(const char* clipId = NULL);
-
-    /**
-     * Stops the AnimationClip with the specified name.
-     *
-     * @param clipId The ID of the AnimationClip to stop. If NULL, stops the default clip.
-     */
-    void stop(const char* clipId = NULL);
-
-    /**
-     * Pauses the AnimationClip with the specified name.
-     *
-     * @param clipId The ID of the AnimationClip to pause. If NULL, pauses the default clip.
-     */
-    void pause(const char* clipId = NULL);
-
-    /**
-     * Returns true if this animation targets the given AnimationTarget.
-     */
-    bool targets(AnimationTarget* target) const;
-
-private:
-
-    /**
-     * Defines a channel which holds the target, target property, curve values, and duration.
-     *
-     * An animation can have 1 or more channels. All typical simple property animations
-     * will have 1 channel. Skeletal animation will have 1 channel per joint to be animated.
-     */
-    class Channel
-    {
-        friend class AnimationClip;
-        friend class Animation;
-        friend class AnimationTarget;
-
-    private:
-
-        Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration);
-        Channel(const Channel& copy, Animation* animation, AnimationTarget* target);
-        Channel(const Channel&); // Hidden copy constructor.
-        ~Channel();
-        Channel& operator=(const Channel&); // Hidden copy assignment operator.
-        Curve* getCurve() const;
-
-        Animation* _animation;                // Reference to the animation this channel belongs to.
-        AnimationTarget* _target;             // The target of this channel.
-        int _propertyId;                      // The target property this channel targets.
-        Curve* _curve;                        // The curve used to represent the animation data.
-        unsigned long _duration;              // The length of the animation (in milliseconds).
-    };
-
-    /**
-     * Hidden copy constructor.
-     */
-    Animation(const Animation& copy);
-
-    /**
-     * Constructor.
-     */
-    Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type);
-
-    /**
-     * Constructor.
-     */
-    Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type);
-
-    /**
-     * Constructor.
-     */
-    Animation(const char* id);
-
-    /**
-     * Destructor.
-     */
-    ~Animation();
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    Animation& operator=(const Animation&);
-
-    /**
-     * Creates the default clip.
-     */
-    void createDefaultClip();
-
-    /**
-     * Creates AnimationClip's for this Animation from the specified Property object.
-     */
-    void createClips(Properties* animationProperties, unsigned int frameCount);
-
-    /**
-     * Adds a clip to this Animation.
-     */
-    void addClip(AnimationClip* clip);
-
-    /**
-     * Finds the clip with the given ID.
-     */
-    AnimationClip* findClip(const char* id) const;
-
-    /**
-     * Creates a channel within this animation.
-     */
-    Channel* createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type);
-
-    /**
-     * Creates a channel within this animation.
-     */
-    Channel* createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type);
-
-    /**
-     * Adds a channel to the animation.
-     */
-    void addChannel(Channel* channel);
-
-    /**
-     * Removes a channel from the animation.
-     */
-    void removeChannel(Channel* channel);
-
-    /**
-     * Sets the rotation offset in a Curve representing a Transform's animation data.
-     */
-    void setTransformRotationOffset(Curve* curve, unsigned int propertyId);
-
-    /**
-     * Clones this animation.
-     *
-     * @param channel The channel to clone and add to the animation.
-     * @param target The target of the animation.
-     *
-     * @return The newly created animation.
-     */
-    Animation* clone(Channel* channel, AnimationTarget* target);
-
-    AnimationController* _controller;       // The AnimationController that this Animation will run on.
-    std::string _id;                        // The Animation's ID.
-    unsigned long _duration;              // the length of the animation (in milliseconds).
-    std::vector<Channel*> _channels;        // The channels within this Animation.
-    AnimationClip* _defaultClip;            // The Animation's default clip.
-    std::vector<AnimationClip*>* _clips;    // All the clips created from this Animation.
-
-};
-
-}
-
-#endif
+#ifndef ANIMATION_H_
+#define ANIMATION_H_
+
+#include "Ref.h"
+#include "Properties.h"
+#include "Curve.h"
+
+namespace gameplay
+{
+
+class AnimationTarget;
+class AnimationController;
+class AnimationClip;
+
+/**
+ * Defines a generic property animation.
+ *
+ * To run an animation you must play an AnimationClip.
+ * Every Animation has the default clip which will run from begin-end time.
+ * You can create additional clips to run only parts of an animation and control
+ * various runtime characteristics, such as repeat count, etc.
+ *
+ * @see http://blackberry.github.io/GamePlay/docs/file-formats.html#wiki-Animation
+ */
+class Animation : public Ref
+{
+    friend class AnimationClip;
+    friend class AnimationTarget;
+    friend class Bundle;
+
+public:
+
+    /**
+     * Gets the Animation's ID.
+     *
+     * @return The Animation's ID.
+     */
+    const char* getId() const;
+
+    /**
+     * Gets the Animation's duration.
+     *
+     * @return The Animation's duration (in milliseconds).
+     */
+    unsigned long getDuration() const;
+
+    /**
+     * Creates an AnimationClip from the Properties object defined at the specified URL,
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
+     *
+     * @param url The URL pointing to the Properties object containing the clip definitions.
+     */
+    void createClips(const char* url);
+
+    /**
+     * Creates an AnimationClip from the Animation.
+     *
+     * @param id The ID to the give the AnimationClip.
+     * @param start The time (in milliseconds) that the AnimationClip will begin from.
+     * @param end The time (in milliseconds) that the AnimationClip will end.
+     *
+     * @return The newly created AnimationClip; NULL if an AnimationClip already exists with the same ID.
+     * @script{create}
+     */
+    AnimationClip* createClip(const char* id, unsigned long start, unsigned long end);
+
+    /**
+     * Finds the AnimationClip with the specified name. If NULL, gets the default clip.
+     *
+     * @param clipId The ID of the AnimationClip to get.
+     *
+     * @return The AnimationClip with the specified ID; NULL if an AnimationClip with the given ID is not found.
+     */
+    AnimationClip* getClip(const char* clipId = NULL);
+
+    /**
+     * Returns the AnimationClip at the given index.
+     *
+     * @param index Index of the clip to return.
+     */
+    AnimationClip* getClip(unsigned int index) const;
+
+    /**
+     * Returns the number of animation clips in this animation.
+     */
+    unsigned int getClipCount() const;
+
+    /**
+     * Plays the AnimationClip with the specified name.
+     *
+     * @param clipId The ID of the AnimationClip to play. If NULL, plays the default clip.
+     */
+    void play(const char* clipId = NULL);
+
+    /**
+     * Stops the AnimationClip with the specified name.
+     *
+     * @param clipId The ID of the AnimationClip to stop. If NULL, stops the default clip.
+     */
+    void stop(const char* clipId = NULL);
+
+    /**
+     * Pauses the AnimationClip with the specified name.
+     *
+     * @param clipId The ID of the AnimationClip to pause. If NULL, pauses the default clip.
+     */
+    void pause(const char* clipId = NULL);
+
+    /**
+     * Returns true if this animation targets the given AnimationTarget.
+     */
+    bool targets(AnimationTarget* target) const;
+
+private:
+
+    /**
+     * Defines a channel which holds the target, target property, curve values, and duration.
+     *
+     * An animation can have 1 or more channels. All typical simple property animations
+     * will have 1 channel. Skeletal animation will have 1 channel per joint to be animated.
+     */
+    class Channel
+    {
+        friend class AnimationClip;
+        friend class Animation;
+        friend class AnimationTarget;
+
+    private:
+
+        Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration);
+        Channel(const Channel& copy, Animation* animation, AnimationTarget* target);
+        Channel(const Channel&); // Hidden copy constructor.
+        ~Channel();
+        Channel& operator=(const Channel&); // Hidden copy assignment operator.
+        Curve* getCurve() const;
+
+        Animation* _animation;                // Reference to the animation this channel belongs to.
+        AnimationTarget* _target;             // The target of this channel.
+        int _propertyId;                      // The target property this channel targets.
+        Curve* _curve;                        // The curve used to represent the animation data.
+        unsigned long _duration;              // The length of the animation (in milliseconds).
+    };
+
+    /**
+     * Hidden copy constructor.
+     */
+    Animation(const Animation& copy);
+
+    /**
+     * Constructor.
+     */
+    Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type);
+
+    /**
+     * Constructor.
+     */
+    Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type);
+
+    /**
+     * Constructor.
+     */
+    Animation(const char* id);
+
+    /**
+     * Destructor.
+     */
+    ~Animation();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Animation& operator=(const Animation&);
+
+    /**
+     * Creates the default clip.
+     */
+    void createDefaultClip();
+
+    /**
+     * Creates AnimationClip's for this Animation from the specified Property object.
+     */
+    void createClips(Properties* animationProperties, unsigned int frameCount);
+
+    /**
+     * Adds a clip to this Animation.
+     */
+    void addClip(AnimationClip* clip);
+
+    /**
+     * Finds the clip with the given ID.
+     */
+    AnimationClip* findClip(const char* id) const;
+
+    /**
+     * Creates a channel within this animation.
+     */
+    Channel* createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, unsigned int type);
+
+    /**
+     * Creates a channel within this animation.
+     */
+    Channel* createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type);
+
+    /**
+     * Adds a channel to the animation.
+     */
+    void addChannel(Channel* channel);
+
+    /**
+     * Removes a channel from the animation.
+     */
+    void removeChannel(Channel* channel);
+
+    /**
+     * Sets the rotation offset in a Curve representing a Transform's animation data.
+     */
+    void setTransformRotationOffset(Curve* curve, unsigned int propertyId);
+
+    /**
+     * Clones this animation.
+     *
+     * @param channel The channel to clone and add to the animation.
+     * @param target The target of the animation.
+     *
+     * @return The newly created animation.
+     */
+    Animation* clone(Channel* channel, AnimationTarget* target);
+
+    AnimationController* _controller;       // The AnimationController that this Animation will run on.
+    std::string _id;                        // The Animation's ID.
+    unsigned long _duration;                // the length of the animation (in milliseconds).
+    std::vector<Channel*> _channels;        // The channels within this Animation.
+    AnimationClip* _defaultClip;            // The Animation's default clip.
+    std::vector<AnimationClip*>* _clips;    // All the clips created from this Animation.
+
+};
+
+}
+
+#endif

+ 666 - 666
gameplay/src/AnimationClip.cpp

@@ -1,666 +1,666 @@
-#include "Base.h"
-#include "AnimationClip.h"
-#include "Animation.h"
-#include "AnimationTarget.h"
-#include "Game.h"
-#include "Quaternion.h"
-#include "ScriptController.h"
-
-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), _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)
-{
-    GP_ASSERT(_animation);
-    GP_ASSERT(0 <= startTime && startTime <= _animation->_duration && 0 <= endTime && endTime <= _animation->_duration);
-
-    for (size_t i = 0, count = _animation->_channels.size(); i < count; i++)
-    {
-        GP_ASSERT(_animation->_channels[i]);
-        GP_ASSERT(_animation->_channels[i]->getCurve());
-        _values.push_back(new AnimationValue(_animation->_channels[i]->getCurve()->getComponentCount()));
-    }
-}
-
-AnimationClip::~AnimationClip()
-{
-    std::vector<AnimationValue*>::iterator valueIter = _values.begin();
-    while (valueIter != _values.end())
-    {
-        SAFE_DELETE(*valueIter);
-        valueIter++;
-    }
-    _values.clear();
-
-    SAFE_RELEASE(_crossFadeToClip);
-    SAFE_DELETE(_beginListeners);
-    SAFE_DELETE(_endListeners);
-
-    if (_scriptListeners)
-    {
-        for (size_t i = 0; i < _scriptListeners->size(); i++)
-        {
-            SAFE_DELETE((*_scriptListeners)[i]);
-        }
-        SAFE_DELETE(_scriptListeners);
-    }
-
-    if (_listeners)
-    {
-        *_listenerItr = _listeners->begin();
-        while (*_listenerItr != _listeners->end())
-        {
-            ListenerEvent* lEvt = **_listenerItr;
-            SAFE_DELETE(lEvt);
-            ++*_listenerItr;
-        }
-        SAFE_DELETE(_listeners);
-    }
-    SAFE_DELETE(_listenerItr);
-}
-
-AnimationClip::ListenerEvent::ListenerEvent(Listener* listener, unsigned long eventTime)
-{
-    _listener = listener;
-    _eventTime = eventTime;
-}
-
-AnimationClip::ListenerEvent::~ListenerEvent()
-{
-}
-
-const char* AnimationClip::getId() const
-{
-    return _id.c_str();
-}
-
-Animation* AnimationClip::getAnimation() const
-{
-    return _animation;
-}
-
-unsigned long AnimationClip::getStartTime() const
-{
-    return _startTime;
-}
-
-unsigned long AnimationClip::getEndTime() const
-{
-    return _endTime;
-}
-
-float AnimationClip::getElapsedTime() const
-{
-    return _elapsedTime;
-}
-
-void AnimationClip::setRepeatCount(float repeatCount)
-{
-    GP_ASSERT(repeatCount == REPEAT_INDEFINITE || repeatCount > 0.0f);
-
-    _repeatCount = repeatCount;
-
-    if (repeatCount == REPEAT_INDEFINITE)
-    {
-        _activeDuration = _duration + _loopBlendTime;
-    }
-    else
-    {
-        _activeDuration = _duration * _repeatCount;
-
-        if (repeatCount > 1.0f && _loopBlendTime > 0.0f)
-            _activeDuration += std::ceil(repeatCount - 1.0f) * _loopBlendTime;
-    }
-}
-
-float AnimationClip::getRepeatCount() const
-{
-    return _repeatCount;
-}
-
-void AnimationClip::setActiveDuration(unsigned long duration)
-{
-    GP_ASSERT(duration > 0.0f);
-
-    if (duration == REPEAT_INDEFINITE)
-    {
-        _activeDuration = _duration + _loopBlendTime;
-    }
-    else
-    {
-        _activeDuration = duration;
-        _repeatCount = (float)_activeDuration / (float)_duration;
-    }
-}
-
-unsigned long AnimationClip::getActiveDuration() const
-{
-    if (_repeatCount == REPEAT_INDEFINITE)
-        return REPEAT_INDEFINITE;
-
-    return _activeDuration;
-}
-
-unsigned long AnimationClip::getDuration() const
-{
-    return _duration;
-}
-
-void AnimationClip::setSpeed(float speed)
-{
-    _speed = speed;
-}
-
-float AnimationClip::getSpeed() const
-{
-    return _speed;
-}
-
-void AnimationClip::setBlendWeight(float blendWeight)
-{
-    _blendWeight = blendWeight;
-}
-
-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));
-}
-
-void AnimationClip::play()
-{
-    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
-    {
-        // If paused, reset the bit and return.
-        if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
-        {
-            resetClipStateBit(CLIP_IS_PAUSED_BIT);
-            return;
-        }
-
-        // If the clip is set to be removed, reset the flag.
-        if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
-            resetClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
-
-        // Set the state bit to restart.
-        setClipStateBit(CLIP_IS_RESTARTED_BIT);
-    }
-    else
-    {
-        setClipStateBit(CLIP_IS_PLAYING_BIT);
-        GP_ASSERT(_animation);
-        GP_ASSERT(_animation->_controller);
-        _animation->_controller->schedule(this);
-    }
-    
-    _timeStarted = Game::getGameTime();
-}
-
-void AnimationClip::stop()
-{
-    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
-    {
-        // Reset the restarted and paused bits. 
-        resetClipStateBit(CLIP_IS_RESTARTED_BIT);
-        resetClipStateBit(CLIP_IS_PAUSED_BIT);
-
-        // Mark the clip to removed from the AnimationController.
-        setClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
-    }
-}
-
-void AnimationClip::pause()
-{
-    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT) && !isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
-    {
-        setClipStateBit(CLIP_IS_PAUSED_BIT);
-    }
-}
-
-void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
-{
-    GP_ASSERT(clip);
-
-    // Check if the given clip is fading into this clip.
-    // We should reset the clip from fading out, and this one from fading in 
-    // in order to start the crossfade back the other way.
-    if (clip->isClipStateBitSet(CLIP_IS_FADING_OUT_BIT) && clip->_crossFadeToClip == this)
-    {
-        clip->resetClipStateBit(CLIP_IS_FADING_OUT_BIT);
-        clip->_crossFadeToClip->resetClipStateBit(CLIP_IS_FADING_IN_BIT);
-        SAFE_RELEASE(clip->_crossFadeToClip);
-    }
-
-    // If I already have a clip I'm fading to and it's not the same as the given clip release it.
-    // Assign the new clip and increase it's ref count.
-    if (_crossFadeToClip)
-    {
-        SAFE_RELEASE(_crossFadeToClip);
-    }
-
-    // Set and initialize the crossfade clip
-    _crossFadeToClip = clip;
-    _crossFadeToClip->addRef();
-    _crossFadeToClip->setClipStateBit(CLIP_IS_FADING_IN_BIT);
-    _crossFadeToClip->_blendWeight = 0.0f;
-    
-    // Set and initialize this clip to fade out
-    setClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT);
-    setClipStateBit(CLIP_IS_FADING_OUT_BIT);
-    _crossFadeOutElapsed = 0.0f;
-    _crossFadeOutDuration = duration;
-    
-    // If this clip is currently not playing, we should start playing it.
-    if (!isClipStateBitSet(CLIP_IS_PLAYING_BIT))
-        play();
-
-    // Start playing the cross fade clip.
-    _crossFadeToClip->play(); 
-}
-
-void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long eventTime)
-{
-    GP_ASSERT(listener);
-    GP_ASSERT(eventTime < _activeDuration);
-
-    ListenerEvent* listenerEvent = new ListenerEvent(listener, eventTime);
-
-    if (!_listeners)
-    {
-        _listeners = new std::list<ListenerEvent*>;
-        _listeners->push_front(listenerEvent);
-
-        _listenerItr = new std::list<ListenerEvent*>::iterator;
-        if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
-            *_listenerItr = _listeners->begin();
-    }
-    else
-    {
-        for (std::list<ListenerEvent*>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
-        {
-            GP_ASSERT(*itr);
-            if (eventTime < (*itr)->_eventTime)
-            {
-                itr = _listeners->insert(itr, listenerEvent);
-
-                // If playing, update the iterator if we need to.
-                // otherwise, it will just be set the next time the clip gets played.
-                if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
-                {
-                    float currentTime = fmodf(_elapsedTime, (float)_duration);
-                    GP_ASSERT(**_listenerItr || *_listenerItr == _listeners->end());
-                    if ((_speed >= 0.0f && currentTime < eventTime && (*_listenerItr == _listeners->end() || eventTime < (**_listenerItr)->_eventTime)) || 
-                        (_speed <= 0 && currentTime > eventTime && (*_listenerItr == _listeners->begin() || eventTime > (**_listenerItr)->_eventTime)))
-                        *_listenerItr = itr;
-                }
-                return;
-            }
-        }
-        _listeners->push_back(listenerEvent);
-    }
-}
-
-void AnimationClip::addBeginListener(AnimationClip::Listener* listener)
-{
-    if (!_beginListeners)
-        _beginListeners = new std::vector<Listener*>;
-
-    GP_ASSERT(listener);
-    _beginListeners->push_back(listener);
-}
-
-void AnimationClip::addEndListener(AnimationClip::Listener* listener)
-{
-    if (!_endListeners)
-        _endListeners = new std::vector<Listener*>;
-
-    GP_ASSERT(listener);
-    _endListeners->push_back(listener);
-}
-
-void AnimationClip::addBeginListener(const char* function)
-{
-    if (!_scriptListeners)
-        _scriptListeners = new std::vector<ScriptListener*>;
-
-    ScriptListener* listener = new ScriptListener(function);
-    _scriptListeners->push_back(listener);
-    addBeginListener(listener);
-}
-
-void AnimationClip::addEndListener(const char* function)
-{
-    if (!_scriptListeners)
-        _scriptListeners = new std::vector<ScriptListener*>;
-
-    ScriptListener* listener = new ScriptListener(function);
-    _scriptListeners->push_back(listener);
-    addEndListener(listener);
-}
-
-void AnimationClip::addListener(const char* function, unsigned long eventTime)
-{
-    if (!_scriptListeners)
-        _scriptListeners = new std::vector<ScriptListener*>;
-
-    ScriptListener* listener = new ScriptListener(function);
-    _scriptListeners->push_back(listener);
-    addListener(listener, eventTime);
-}
-
-bool AnimationClip::update(float elapsedTime)
-{
-    if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
-    {
-        return false;
-    }
-
-    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;
-    }
-
-    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);
-
-        // 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
-        {
-            // Animation is running normally.
-            currentTime = fmodf(_elapsedTime, _duration + _loopBlendTime);
-        }
-    }
-
-    // Notify any listeners of Animation events.
-    if (_listeners)
-    {
-        GP_ASSERT(_listenerItr);
-
-        if (_speed >= 0.0f)
-        {
-            while (*_listenerItr != _listeners->end() && _elapsedTime >= (long) (**_listenerItr)->_eventTime)
-            {
-                GP_ASSERT(_listenerItr);
-                GP_ASSERT(**_listenerItr);
-                GP_ASSERT((**_listenerItr)->_listener);
-
-                (**_listenerItr)->_listener->animationEvent(this, Listener::TIME);
-                ++*_listenerItr;
-            }
-        }
-        else
-        {
-            while (*_listenerItr != _listeners->begin() && _elapsedTime <= (long) (**_listenerItr)->_eventTime)
-            {
-                GP_ASSERT(_listenerItr);
-                GP_ASSERT(**_listenerItr);
-                GP_ASSERT((**_listenerItr)->_listener);
-
-                (**_listenerItr)->_listener->animationEvent(this, Listener::TIME);
-                --*_listenerItr;
-            }
-        }
-    }
-
-    // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
-    GP_ASSERT(_animation);
-
-    // 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);
-        GP_ASSERT(_crossFadeOutDuration > 0);
-
-        if (isClipStateBitSet(CLIP_IS_FADING_OUT_STARTED_BIT)) // Calculate elapsed time since the fade out begin.
-        {
-            GP_ASSERT(_crossFadeToClip);
-            _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * fabs(_speed); 
-            resetClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT);
-        }
-        else
-        {
-            // continue tracking elapsed time.
-            _crossFadeOutElapsed += elapsedTime * fabs(_speed);
-        }
-
-        if (_crossFadeOutElapsed < _crossFadeOutDuration)
-        {
-            // Calculate this clip's blend weight.
-            float tempBlendWeight = ((float)_crossFadeOutDuration - _crossFadeOutElapsed) / (float)_crossFadeOutDuration;
-            
-            // If this clip is fading in, adjust the crossfade clip's weight to be a percentage of your current blend weight
-            if (isClipStateBitSet(CLIP_IS_FADING_IN_BIT))
-            {
-                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight) * _blendWeight;
-                _blendWeight -= _crossFadeToClip->_blendWeight;
-            }
-            else
-            {
-                // Just set the blend weight.
-                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight);
-                _blendWeight = tempBlendWeight;
-            }
-        }
-        else
-        {
-            // Fade is done.
-            _crossFadeToClip->_blendWeight = 1.0f;
-            _blendWeight = 0.0f;
-            resetClipStateBit(CLIP_IS_STARTED_BIT);            
-            resetClipStateBit(CLIP_IS_FADING_OUT_BIT);
-            _crossFadeToClip->resetClipStateBit(CLIP_IS_FADING_IN_BIT);
-            SAFE_RELEASE(_crossFadeToClip);
-        }
-    }
-    
-    // Evaluate this clip.
-    Animation::Channel* channel = NULL;
-    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];
-        GP_ASSERT(channel);
-        target = channel->_target;
-        GP_ASSERT(target);
-        value = _values[i];
-        GP_ASSERT(value);
-
-        // Evaluate the point on Curve
-        GP_ASSERT(channel->getCurve());
-        channel->getCurve()->evaluate(percentComplete, percentageStart, percentageEnd, percentageBlend, value->_value);
-
-        // Set the animation value on the target property.
-        target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
-    }
-
-    // When ended. Probably should move to it's own method so we can call it when the clip is ended early.
-    if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT) || !isClipStateBitSet(CLIP_IS_STARTED_BIT))
-    {
-        onEnd();
-        return true;
-    }
-
-    return false;
-}
-
-void AnimationClip::onBegin()
-{
-    addRef();
-
-    // Initialize animation to play.
-    setClipStateBit(CLIP_IS_STARTED_BIT);
-    if (_speed >= 0)
-    {
-        _elapsedTime = (Game::getGameTime() - _timeStarted) * _speed;
-
-        if (_listeners)
-            *_listenerItr = _listeners->begin();
-    }
-    else
-    {
-        _elapsedTime = _activeDuration + (Game::getGameTime() - _timeStarted) * _speed;
-
-        if (_listeners)
-            *_listenerItr = _listeners->end();
-    }
-    
-    // Notify begin listeners if any.
-    if (_beginListeners)
-    {
-        std::vector<Listener*>::iterator listener = _beginListeners->begin();
-        while (listener != _beginListeners->end())
-        {
-            GP_ASSERT(*listener);
-            (*listener)->animationEvent(this, Listener::BEGIN);
-            listener++;
-        }
-    }
-
-    release();
-}
-
-void AnimationClip::onEnd()
-{
-    addRef();
-
-    _blendWeight = 1.0f;
-    resetClipStateBit(CLIP_ALL_BITS);
-
-    // Notify end listeners if any.
-    if (_endListeners)
-    {
-        std::vector<Listener*>::iterator listener = _endListeners->begin();
-        while (listener != _endListeners->end())
-        {
-            GP_ASSERT(*listener);
-            (*listener)->animationEvent(this, Listener::END);
-            listener++;
-        }
-    }
-
-    release();
-}
-
-bool AnimationClip::isClipStateBitSet(unsigned char bit) const
-{
-    return (_stateBits & bit) == bit;
-}
-
-void AnimationClip::setClipStateBit(unsigned char bit)
-{
-    _stateBits |= bit;
-}
-
-void AnimationClip::resetClipStateBit(unsigned char bit)
-{
-    _stateBits &= ~bit;
-}
-
-AnimationClip* AnimationClip::clone(Animation* animation) const
-{
-    // Don't clone the elapsed time, listeners or crossfade information.
-    AnimationClip* newClip = new AnimationClip(getId(), animation, getStartTime(), getEndTime());
-    newClip->setSpeed(getSpeed());
-    newClip->setRepeatCount(getRepeatCount());
-    newClip->setBlendWeight(getBlendWeight());
-    
-    size_t size = _values.size();
-    newClip->_values.resize(size, NULL);
-    for (size_t i = 0; i < size; ++i)
-    {
-        if (newClip->_values[i] == NULL)
-        {
-            newClip->_values[i] = new AnimationValue(*_values[i]);
-        }
-        else
-        {
-            *newClip->_values[i] = *_values[i];
-        }
-    }
-    return newClip;
-}
-
-AnimationClip::ScriptListener::ScriptListener(const std::string& function)
-{
-    // Store the function name.
-    this->function = Game::getInstance()->getScriptController()->loadUrl(function.c_str());
-}
-
-void AnimationClip::ScriptListener::animationEvent(AnimationClip* clip, EventType type)
-{
-    Game::getInstance()->getScriptController()->executeFunction<void>(function.c_str(), "<AnimationClip>[AnimationClip::Listener::EventType]", clip, type);
-}
-
-
-}
+#include "Base.h"
+#include "AnimationClip.h"
+#include "Animation.h"
+#include "AnimationTarget.h"
+#include "Game.h"
+#include "Quaternion.h"
+#include "ScriptController.h"
+
+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), _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)
+{
+    GP_ASSERT(_animation);
+    GP_ASSERT(0 <= startTime && startTime <= _animation->_duration && 0 <= endTime && endTime <= _animation->_duration);
+
+    for (size_t i = 0, count = _animation->_channels.size(); i < count; i++)
+    {
+        GP_ASSERT(_animation->_channels[i]);
+        GP_ASSERT(_animation->_channels[i]->getCurve());
+        _values.push_back(new AnimationValue(_animation->_channels[i]->getCurve()->getComponentCount()));
+    }
+}
+
+AnimationClip::~AnimationClip()
+{
+    std::vector<AnimationValue*>::iterator valueIter = _values.begin();
+    while (valueIter != _values.end())
+    {
+        SAFE_DELETE(*valueIter);
+        valueIter++;
+    }
+    _values.clear();
+
+    SAFE_RELEASE(_crossFadeToClip);
+    SAFE_DELETE(_beginListeners);
+    SAFE_DELETE(_endListeners);
+
+    if (_scriptListeners)
+    {
+        for (size_t i = 0; i < _scriptListeners->size(); i++)
+        {
+            SAFE_DELETE((*_scriptListeners)[i]);
+        }
+        SAFE_DELETE(_scriptListeners);
+    }
+
+    if (_listeners)
+    {
+        *_listenerItr = _listeners->begin();
+        while (*_listenerItr != _listeners->end())
+        {
+            ListenerEvent* lEvt = **_listenerItr;
+            SAFE_DELETE(lEvt);
+            ++*_listenerItr;
+        }
+        SAFE_DELETE(_listeners);
+    }
+    SAFE_DELETE(_listenerItr);
+}
+
+AnimationClip::ListenerEvent::ListenerEvent(Listener* listener, unsigned long eventTime)
+{
+    _listener = listener;
+    _eventTime = eventTime;
+}
+
+AnimationClip::ListenerEvent::~ListenerEvent()
+{
+}
+
+const char* AnimationClip::getId() const
+{
+    return _id.c_str();
+}
+
+Animation* AnimationClip::getAnimation() const
+{
+    return _animation;
+}
+
+unsigned long AnimationClip::getStartTime() const
+{
+    return _startTime;
+}
+
+unsigned long AnimationClip::getEndTime() const
+{
+    return _endTime;
+}
+
+float AnimationClip::getElapsedTime() const
+{
+    return _elapsedTime;
+}
+
+void AnimationClip::setRepeatCount(float repeatCount)
+{
+    GP_ASSERT(repeatCount == REPEAT_INDEFINITE || repeatCount > 0.0f);
+
+    _repeatCount = repeatCount;
+
+    if (repeatCount == REPEAT_INDEFINITE)
+    {
+        _activeDuration = _duration + _loopBlendTime;
+    }
+    else
+    {
+        _activeDuration = _duration * _repeatCount;
+
+        if (repeatCount > 1.0f && _loopBlendTime > 0.0f)
+            _activeDuration += std::ceil(repeatCount - 1.0f) * _loopBlendTime;
+    }
+}
+
+float AnimationClip::getRepeatCount() const
+{
+    return _repeatCount;
+}
+
+void AnimationClip::setActiveDuration(unsigned long duration)
+{
+    GP_ASSERT(duration >= 0);
+
+    if (duration == REPEAT_INDEFINITE)
+    {
+        _activeDuration = _duration + _loopBlendTime;
+    }
+    else
+    {
+        _activeDuration = duration;
+        _repeatCount = (float)_activeDuration / (float)_duration;
+    }
+}
+
+unsigned long AnimationClip::getActiveDuration() const
+{
+    if (_repeatCount == REPEAT_INDEFINITE)
+        return REPEAT_INDEFINITE;
+
+    return _activeDuration;
+}
+
+unsigned long AnimationClip::getDuration() const
+{
+    return _duration;
+}
+
+void AnimationClip::setSpeed(float speed)
+{
+    _speed = speed;
+}
+
+float AnimationClip::getSpeed() const
+{
+    return _speed;
+}
+
+void AnimationClip::setBlendWeight(float blendWeight)
+{
+    _blendWeight = blendWeight;
+}
+
+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));
+}
+
+void AnimationClip::play()
+{
+    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
+    {
+        // If paused, reset the bit and return.
+        if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
+        {
+            resetClipStateBit(CLIP_IS_PAUSED_BIT);
+            return;
+        }
+
+        // If the clip is set to be removed, reset the flag.
+        if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+            resetClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
+
+        // Set the state bit to restart.
+        setClipStateBit(CLIP_IS_RESTARTED_BIT);
+    }
+    else
+    {
+        setClipStateBit(CLIP_IS_PLAYING_BIT);
+        GP_ASSERT(_animation);
+        GP_ASSERT(_animation->_controller);
+        _animation->_controller->schedule(this);
+    }
+    
+    _timeStarted = Game::getGameTime();
+}
+
+void AnimationClip::stop()
+{
+    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
+    {
+        // Reset the restarted and paused bits. 
+        resetClipStateBit(CLIP_IS_RESTARTED_BIT);
+        resetClipStateBit(CLIP_IS_PAUSED_BIT);
+
+        // Mark the clip to removed from the AnimationController.
+        setClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
+    }
+}
+
+void AnimationClip::pause()
+{
+    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT) && !isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+    {
+        setClipStateBit(CLIP_IS_PAUSED_BIT);
+    }
+}
+
+void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
+{
+    GP_ASSERT(clip);
+
+    // Check if the given clip is fading into this clip.
+    // We should reset the clip from fading out, and this one from fading in 
+    // in order to start the crossfade back the other way.
+    if (clip->isClipStateBitSet(CLIP_IS_FADING_OUT_BIT) && clip->_crossFadeToClip == this)
+    {
+        clip->resetClipStateBit(CLIP_IS_FADING_OUT_BIT);
+        clip->_crossFadeToClip->resetClipStateBit(CLIP_IS_FADING_IN_BIT);
+        SAFE_RELEASE(clip->_crossFadeToClip);
+    }
+
+    // If I already have a clip I'm fading to and it's not the same as the given clip release it.
+    // Assign the new clip and increase it's ref count.
+    if (_crossFadeToClip)
+    {
+        SAFE_RELEASE(_crossFadeToClip);
+    }
+
+    // Set and initialize the crossfade clip
+    _crossFadeToClip = clip;
+    _crossFadeToClip->addRef();
+    _crossFadeToClip->setClipStateBit(CLIP_IS_FADING_IN_BIT);
+    _crossFadeToClip->_blendWeight = 0.0f;
+    
+    // Set and initialize this clip to fade out
+    setClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT);
+    setClipStateBit(CLIP_IS_FADING_OUT_BIT);
+    _crossFadeOutElapsed = 0.0f;
+    _crossFadeOutDuration = duration;
+    
+    // If this clip is currently not playing, we should start playing it.
+    if (!isClipStateBitSet(CLIP_IS_PLAYING_BIT))
+        play();
+
+    // Start playing the cross fade clip.
+    _crossFadeToClip->play(); 
+}
+
+void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long eventTime)
+{
+    GP_ASSERT(listener);
+    GP_ASSERT(eventTime < _activeDuration);
+
+    ListenerEvent* listenerEvent = new ListenerEvent(listener, eventTime);
+
+    if (!_listeners)
+    {
+        _listeners = new std::list<ListenerEvent*>;
+        _listeners->push_front(listenerEvent);
+
+        _listenerItr = new std::list<ListenerEvent*>::iterator;
+        if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
+            *_listenerItr = _listeners->begin();
+    }
+    else
+    {
+        for (std::list<ListenerEvent*>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
+        {
+            GP_ASSERT(*itr);
+            if (eventTime < (*itr)->_eventTime)
+            {
+                itr = _listeners->insert(itr, listenerEvent);
+
+                // If playing, update the iterator if we need to.
+                // otherwise, it will just be set the next time the clip gets played.
+                if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
+                {
+                    float currentTime = fmodf(_elapsedTime, (float)_duration);
+                    GP_ASSERT(**_listenerItr || *_listenerItr == _listeners->end());
+                    if ((_speed >= 0.0f && currentTime < eventTime && (*_listenerItr == _listeners->end() || eventTime < (**_listenerItr)->_eventTime)) || 
+                        (_speed <= 0 && currentTime > eventTime && (*_listenerItr == _listeners->begin() || eventTime > (**_listenerItr)->_eventTime)))
+                        *_listenerItr = itr;
+                }
+                return;
+            }
+        }
+        _listeners->push_back(listenerEvent);
+    }
+}
+
+void AnimationClip::addBeginListener(AnimationClip::Listener* listener)
+{
+    if (!_beginListeners)
+        _beginListeners = new std::vector<Listener*>;
+
+    GP_ASSERT(listener);
+    _beginListeners->push_back(listener);
+}
+
+void AnimationClip::addEndListener(AnimationClip::Listener* listener)
+{
+    if (!_endListeners)
+        _endListeners = new std::vector<Listener*>;
+
+    GP_ASSERT(listener);
+    _endListeners->push_back(listener);
+}
+
+void AnimationClip::addBeginListener(const char* function)
+{
+    if (!_scriptListeners)
+        _scriptListeners = new std::vector<ScriptListener*>;
+
+    ScriptListener* listener = new ScriptListener(function);
+    _scriptListeners->push_back(listener);
+    addBeginListener(listener);
+}
+
+void AnimationClip::addEndListener(const char* function)
+{
+    if (!_scriptListeners)
+        _scriptListeners = new std::vector<ScriptListener*>;
+
+    ScriptListener* listener = new ScriptListener(function);
+    _scriptListeners->push_back(listener);
+    addEndListener(listener);
+}
+
+void AnimationClip::addListener(const char* function, unsigned long eventTime)
+{
+    if (!_scriptListeners)
+        _scriptListeners = new std::vector<ScriptListener*>;
+
+    ScriptListener* listener = new ScriptListener(function);
+    _scriptListeners->push_back(listener);
+    addListener(listener, eventTime);
+}
+
+bool AnimationClip::update(float elapsedTime)
+{
+    if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
+    {
+        return false;
+    }
+
+    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;
+    }
+
+    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);
+
+        // 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
+        {
+            // Animation is running normally.
+            currentTime = fmodf(_elapsedTime, _duration + _loopBlendTime);
+        }
+    }
+
+    // Notify any listeners of Animation events.
+    if (_listeners)
+    {
+        GP_ASSERT(_listenerItr);
+
+        if (_speed >= 0.0f)
+        {
+            while (*_listenerItr != _listeners->end() && _elapsedTime >= (long) (**_listenerItr)->_eventTime)
+            {
+                GP_ASSERT(_listenerItr);
+                GP_ASSERT(**_listenerItr);
+                GP_ASSERT((**_listenerItr)->_listener);
+
+                (**_listenerItr)->_listener->animationEvent(this, Listener::TIME);
+                ++*_listenerItr;
+            }
+        }
+        else
+        {
+            while (*_listenerItr != _listeners->begin() && _elapsedTime <= (long) (**_listenerItr)->_eventTime)
+            {
+                GP_ASSERT(_listenerItr);
+                GP_ASSERT(**_listenerItr);
+                GP_ASSERT((**_listenerItr)->_listener);
+
+                (**_listenerItr)->_listener->animationEvent(this, Listener::TIME);
+                --*_listenerItr;
+            }
+        }
+    }
+
+    // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
+    GP_ASSERT(_animation);
+
+    // 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);
+        GP_ASSERT(_crossFadeOutDuration > 0);
+
+        if (isClipStateBitSet(CLIP_IS_FADING_OUT_STARTED_BIT)) // Calculate elapsed time since the fade out begin.
+        {
+            GP_ASSERT(_crossFadeToClip);
+            _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * fabs(_speed); 
+            resetClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT);
+        }
+        else
+        {
+            // continue tracking elapsed time.
+            _crossFadeOutElapsed += elapsedTime * fabs(_speed);
+        }
+
+        if (_crossFadeOutElapsed < _crossFadeOutDuration)
+        {
+            // Calculate this clip's blend weight.
+            float tempBlendWeight = ((float)_crossFadeOutDuration - _crossFadeOutElapsed) / (float)_crossFadeOutDuration;
+            
+            // If this clip is fading in, adjust the crossfade clip's weight to be a percentage of your current blend weight
+            if (isClipStateBitSet(CLIP_IS_FADING_IN_BIT))
+            {
+                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight) * _blendWeight;
+                _blendWeight -= _crossFadeToClip->_blendWeight;
+            }
+            else
+            {
+                // Just set the blend weight.
+                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight);
+                _blendWeight = tempBlendWeight;
+            }
+        }
+        else
+        {
+            // Fade is done.
+            _crossFadeToClip->_blendWeight = 1.0f;
+            _blendWeight = 0.0f;
+            resetClipStateBit(CLIP_IS_STARTED_BIT);            
+            resetClipStateBit(CLIP_IS_FADING_OUT_BIT);
+            _crossFadeToClip->resetClipStateBit(CLIP_IS_FADING_IN_BIT);
+            SAFE_RELEASE(_crossFadeToClip);
+        }
+    }
+    
+    // Evaluate this clip.
+    Animation::Channel* channel = NULL;
+    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];
+        GP_ASSERT(channel);
+        target = channel->_target;
+        GP_ASSERT(target);
+        value = _values[i];
+        GP_ASSERT(value);
+
+        // Evaluate the point on Curve
+        GP_ASSERT(channel->getCurve());
+        channel->getCurve()->evaluate(percentComplete, percentageStart, percentageEnd, percentageBlend, value->_value);
+
+        // Set the animation value on the target property.
+        target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
+    }
+
+    // When ended. Probably should move to it's own method so we can call it when the clip is ended early.
+    if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT) || !isClipStateBitSet(CLIP_IS_STARTED_BIT))
+    {
+        onEnd();
+        return true;
+    }
+
+    return false;
+}
+
+void AnimationClip::onBegin()
+{
+    addRef();
+
+    // Initialize animation to play.
+    setClipStateBit(CLIP_IS_STARTED_BIT);
+    if (_speed >= 0)
+    {
+        _elapsedTime = (Game::getGameTime() - _timeStarted) * _speed;
+
+        if (_listeners)
+            *_listenerItr = _listeners->begin();
+    }
+    else
+    {
+        _elapsedTime = _activeDuration + (Game::getGameTime() - _timeStarted) * _speed;
+
+        if (_listeners)
+            *_listenerItr = _listeners->end();
+    }
+    
+    // Notify begin listeners if any.
+    if (_beginListeners)
+    {
+        std::vector<Listener*>::iterator listener = _beginListeners->begin();
+        while (listener != _beginListeners->end())
+        {
+            GP_ASSERT(*listener);
+            (*listener)->animationEvent(this, Listener::BEGIN);
+            listener++;
+        }
+    }
+
+    release();
+}
+
+void AnimationClip::onEnd()
+{
+    addRef();
+
+    _blendWeight = 1.0f;
+    resetClipStateBit(CLIP_ALL_BITS);
+
+    // Notify end listeners if any.
+    if (_endListeners)
+    {
+        std::vector<Listener*>::iterator listener = _endListeners->begin();
+        while (listener != _endListeners->end())
+        {
+            GP_ASSERT(*listener);
+            (*listener)->animationEvent(this, Listener::END);
+            listener++;
+        }
+    }
+
+    release();
+}
+
+bool AnimationClip::isClipStateBitSet(unsigned char bit) const
+{
+    return (_stateBits & bit) == bit;
+}
+
+void AnimationClip::setClipStateBit(unsigned char bit)
+{
+    _stateBits |= bit;
+}
+
+void AnimationClip::resetClipStateBit(unsigned char bit)
+{
+    _stateBits &= ~bit;
+}
+
+AnimationClip* AnimationClip::clone(Animation* animation) const
+{
+    // Don't clone the elapsed time, listeners or crossfade information.
+    AnimationClip* newClip = new AnimationClip(getId(), animation, getStartTime(), getEndTime());
+    newClip->setSpeed(getSpeed());
+    newClip->setRepeatCount(getRepeatCount());
+    newClip->setBlendWeight(getBlendWeight());
+    
+    size_t size = _values.size();
+    newClip->_values.resize(size, NULL);
+    for (size_t i = 0; i < size; ++i)
+    {
+        if (newClip->_values[i] == NULL)
+        {
+            newClip->_values[i] = new AnimationValue(*_values[i]);
+        }
+        else
+        {
+            *newClip->_values[i] = *_values[i];
+        }
+    }
+    return newClip;
+}
+
+AnimationClip::ScriptListener::ScriptListener(const std::string& function)
+{
+    // Store the function name.
+    this->function = Game::getInstance()->getScriptController()->loadUrl(function.c_str());
+}
+
+void AnimationClip::ScriptListener::animationEvent(AnimationClip* clip, EventType type)
+{
+    Game::getInstance()->getScriptController()->executeFunction<void>(function.c_str(), "<AnimationClip>[AnimationClip::Listener::EventType]", clip, type);
+}
+
+
+}

+ 430 - 430
gameplay/src/AnimationClip.h

@@ -1,430 +1,430 @@
-#ifndef ANIMATIONCLIP_H_
-#define ANIMATIONCLIP_H_
-
-#include "Base.h"
-#include "AnimationValue.h"
-#include "Curve.h"
-#include "Animation.h"
-
-namespace gameplay
-{
-
-class Animation;
-class AnimationValue;
-class ScriptListener;
-
-/**
- * Defines the runtime session of an Animation to be played.
- */
-class AnimationClip : public Ref
-{
-    friend class AnimationController;
-    friend class Animation;
-
-public:
-
-    /**
-     * Defines a constant for indefinitely repeating an AnimationClip.
-     */
-    static const unsigned int REPEAT_INDEFINITE = 0;
-
-    /**
-     * Defines an animation event listener.
-     */
-    class Listener
-    {
-        friend class AnimationClip;
-
-    public:
-
-        /*
-         * Constructor.
-         */
-        Listener() 
-        {
-        }
-
-        /**
-         * The type of animation event.
-         */
-        enum EventType 
-        {
-            /**
-             * Event fired when the clip begins.
-             */
-            BEGIN,
-
-            /**
-             * Event fired when the clip ends.
-             */
-            END,
-
-            /**
-             * Event fired at a specified time during a clip update.
-             */
-            TIME
-        };
-        
-        /*
-         * Destructor.
-         */
-        virtual ~Listener() { }
-
-        /**
-         * Handles when animation event occurs.
-         */
-        virtual void animationEvent(AnimationClip* clip, EventType type) = 0;
-    };
-
-    /**
-     * Gets the AnimationClip's ID.
-     *
-     * @return The AnimationClip's ID.
-     */
-    const char* getId() const;
-
-    /**
-     * Gets the Animation that this AnimationClip was created from.
-     * 
-     * @return The Animation that this clip was created from.
-     */
-    Animation* getAnimation() const;
-
-    /**
-     * Gets the AnimationClip's start time.
-     *
-     * @return The time (in milliseconds) that the AnimationClip starts playing from.
-     */
-    unsigned long getStartTime() const;
-
-    /**
-     * Gets the AnimationClip's end time.
-     * 
-     * @return The time (in milliseconds) that the AnimationClip will end.
-     */
-    unsigned long getEndTime() const;
-
-    /**
-     * Gets the AnimationClip's elapsed time.
-     *
-     * @return The elapsed time of the AnimationClip (in milliseconds).
-     */
-    float getElapsedTime() const;
-
-    /**
-     * Sets the AnimationClip's repeat count. Overrides repeat duration.
-     *
-     * Use REPEAT_INDEFINITE to play the AnimationClip indefinitely.
-     * 
-     * @param repeatCount The repeat count to set on the AnimationClip.
-     */
-    void setRepeatCount(float repeatCount);
-
-    /**
-     * Gets the AnimationClip's repeat count.
-     *
-     * @return The repeat count that is set on the AnimationClip.
-     */
-    float getRepeatCount() const;
-
-    /**
-     * Sets the AnimationClip's active duration. Overrides repeat count.
-     *
-     * Use REPEAT_INDEFINITE to play the AnimationClip indefinitely.
-     *
-     * @param duration The active duration that is set on the AnimationClip, in milliseconds.
-     */
-    void setActiveDuration(unsigned long duration);
-
-    /**
-     * Gets the AnimationClip's active duration.
-     * 
-     * @return the AnimationClip's active duration.
-     */
-    unsigned long getActiveDuration() const;
-
-    /**
-     * Gets the AnimationClip's duration.
-     *
-     * @return the AnimationClip's duration, in milliseconds.
-     */
-    unsigned long getDuration() const;
-
-    /**
-     * Set the AnimationClip's running speed. 
-     *
-     * @param speed The clips running speed.
-     */
-    void setSpeed(float speed);
-
-    /**
-     * Gets the AninimationClip's running speed.
-     *
-     * @return The AninimationClip's running speed.
-     */
-    float getSpeed() const;
-
-    /**
-     * Sets the blend weight of the AnimationClip.
-     *
-     * @param blendWeight The blend weight to apply to the clip.
-     */
-    void setBlendWeight(float blendWeight);
-
-    /** 
-     * Gets the blend weight of the AnimationClip.
-     *
-     * @return The blendweight of the AnimationClip.
-     */
-    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.
-     *
-     * @return true if the AnimationClip is playing; false if the AnimationClip is not playing.
-     */
-    bool isPlaying() const;
-
-    /**
-     * Plays the AnimationClip.
-     */
-    void play();
-
-    /**
-     * Stops the AnimationClip.
-     */
-    void stop();
-
-    /**
-     * Pauses the AnimationClip.
-     */
-    void pause();
-
-    /**
-     * Fades this clip out, and the specified clip in over the given duration.
-     *
-     * @param clip The clip to fade into.
-     * @param duration The duration of the fade.
-     */
-    void crossFade(AnimationClip* clip, unsigned long duration);
-
-    /**
-     * Adds an animation begin listener.
-     *
-     * @param listener The listener to be called when an AnimationClip begins.
-     */
-    void addBeginListener(AnimationClip::Listener* listener);
-
-    /**
-     * Adds an animation end listener.
-     *
-     * @param listener The listener to be called when an AnimationClip ends.
-     */
-    void addEndListener(AnimationClip::Listener* listener);
-
-    /**
-     * Adds an animation listener to be called back at the specified eventTime during the playback 
-     * of the AnimationClip.
-     *
-     * @param listener The listener to be called when the AnimationClip reaches the 
-     *      specified time in its playback.
-     * @param eventTime The time the listener will be called during the playback of the AnimationClip. 
-     *      Must be between 0 and the duration of the AnimationClip.
-     */
-    void addListener(AnimationClip::Listener* listener, unsigned long eventTime);
-
-    /**
-     * Adds an animation begin listener.
-     * 
-     * Note: the given Lua function must have the same function signature as AnimationClip::Listener::animationEvent.
-     *
-     * @param function The Lua script function to be called when an AnimationClip begins.
-     */
-    void addBeginListener(const char* function);
-
-    /**
-     * Adds an animation end listener.
-     * 
-     * Note: the given Lua function must have the same function signature as AnimationClip::Listener::animationEvent.
-     *
-     * @param function The Lua script function to be called when an AnimationClip ends.
-     */
-    void addEndListener(const char* function);
-
-    /**
-     * Adds an animation listener to be called back at the specified eventTime during the playback 
-     * of the AnimationClip.
-     * 
-     * Note: the given Lua function must have the same function signature as AnimationClip::Listener::animationEvent.
-     * 
-     * @param function The Lua script function to be called when an AnimationClip reaches the 
-     *      specified time in its playback.
-     * @param eventTime The time the listener will be called during the playback of the AnimationClip. 
-     *      Must be between 0 and the duration of the AnimationClip.
-     */
-    void addListener(const char* function, unsigned long eventTime);
-
-private:
-    
-    static const unsigned char CLIP_IS_PLAYING_BIT = 0x01;             // Bit representing whether AnimationClip is a running clip in AnimationController
-    static const unsigned char CLIP_IS_STARTED_BIT = 0x02;             // Bit representing whether the AnimationClip has actually been started (ie: received first call to update())
-    static const unsigned char CLIP_IS_FADING_OUT_STARTED_BIT = 0x04;  // Bit representing that a cross fade has started.
-    static const unsigned char CLIP_IS_FADING_OUT_BIT = 0x08;          // Bit representing whether the clip is fading out.
-    static const unsigned char CLIP_IS_FADING_IN_BIT = 0x10;           // Bit representing whether the clip is fading out.
-    static const unsigned char CLIP_IS_MARKED_FOR_REMOVAL_BIT = 0x20;  // Bit representing whether the clip has ended and should be removed from the AnimationController.
-    static const unsigned char CLIP_IS_RESTARTED_BIT = 0x40;           // Bit representing if the clip should be restarted by the AnimationController.
-    static const unsigned char CLIP_IS_PAUSED_BIT = 0x80;              // Bit representing if the clip is currently paused.
-    static const unsigned char CLIP_ALL_BITS = 0xFF;                   // Bit mask for all the state bits.
-
-    /**
-     * ListenerEvent.
-     *
-     * Internal structure used for storing the event time at which an AnimationClip::Listener should be called back.
-     */
-    struct ListenerEvent
-    {
-        /** 
-         * Constructor.
-         */
-        ListenerEvent(Listener* listener, unsigned long eventTime);
-
-        /**
-         * Destructor.
-         */
-        ~ListenerEvent();
-
-        /**
-         * Hidden copy assignment operator.
-         */
-        ListenerEvent& operator=(const ListenerEvent&);
-
-        Listener* _listener;        // This listener to call back when this event is triggered.
-        unsigned long _eventTime;   // The time at which the listener will be called back at during the playback of the AnimationClip.
-    };
-
-    /**
-     * Listener implementation for script callbacks.
-     */
-    struct ScriptListener : public AnimationClip::Listener
-    {
-        /**
-         * Constructor.
-         */
-        ScriptListener(const std::string& function);
-
-        /**
-         * @see AnimationClip::Listener::animationEvent
-         */
-        void animationEvent(AnimationClip* clip, EventType type);
-
-        /** The function to call back when an animation event occurs. */
-        std::string function;
-    };
-
-    /**
-     * Constructor.
-     */
-    AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime);
-
-    /**
-     * Constructor.
-     */
-    AnimationClip();
-
-    /**
-     * Constructor.
-     */
-    AnimationClip(const AnimationClip& copy);
-
-    /**
-     * Destructor.
-     */
-    ~AnimationClip();
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    AnimationClip& operator=(const AnimationClip&);
-
-    /**
-     * Updates the animation with the elapsed time.
-     */
-    bool update(float elapsedTime);
-
-    /**
-     * Handles when the AnimationClip begins.
-     */
-    void onBegin();
-
-    /**
-     * Handles when the AnimationClip ends.
-     */
-    void onEnd();
-
-    /**
-     * Determines whether the given bit is set in the AnimationClip's state.
-     */
-    bool isClipStateBitSet(unsigned char bit) const;
-
-    /**
-     * Sets the given bit in the AnimationClip's state.
-     */
-    void setClipStateBit(unsigned char bit);
-
-    /**
-     * Resets the given bit in the AnimationClip's state.
-     */
-    void resetClipStateBit(unsigned char bit);
-
-    /**
-     * Clones the animation clip.
-     * 
-     * @param animation The animation that the new clip belongs to.
-     * 
-     * @return The newly created animation clip.
-     */
-    AnimationClip* clone(Animation* animation) const;
-
-    std::string _id;                                    // AnimationClip ID.
-    Animation* _animation;                              // The Animation this clip is created from.
-    unsigned long _startTime;                           // Start time of the clip.
-    unsigned long _endTime;                             // End time of the clip.
-    unsigned long _duration;                            // The total duration.
-    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.
-    float _elapsedTime;                                 // Time elapsed while the clip is running.
-    AnimationClip* _crossFadeToClip;                    // The clip to cross fade to.
-    float _crossFadeOutElapsed;                         // The amount of time that has elapsed for the crossfade.
-    unsigned long _crossFadeOutDuration;                // The duration of the cross fade.
-    float _blendWeight;                                 // The clip's blendweight.
-    std::vector<AnimationValue*> _values;               // AnimationValue holder.
-    std::vector<Listener*>* _beginListeners;            // Collection of begin listeners on the clip.
-    std::vector<Listener*>* _endListeners;              // Collection of end listeners on the clip.
-    std::list<ListenerEvent*>* _listeners;              // Ordered collection of listeners on the clip.
-    std::list<ListenerEvent*>::iterator* _listenerItr;  // Iterator that points to the next listener event to be triggered.
-    std::vector<ScriptListener*>* _scriptListeners;     // Collection of listeners that are bound to Lua script functions.
-};
-
-}
-
-#endif
+#ifndef ANIMATIONCLIP_H_
+#define ANIMATIONCLIP_H_
+
+#include "Base.h"
+#include "AnimationValue.h"
+#include "Curve.h"
+#include "Animation.h"
+
+namespace gameplay
+{
+
+class Animation;
+class AnimationValue;
+class ScriptListener;
+
+/**
+ * Defines the runtime session of an Animation to be played.
+ */
+class AnimationClip : public Ref
+{
+    friend class AnimationController;
+    friend class Animation;
+
+public:
+
+    /**
+     * Defines a constant for indefinitely repeating an AnimationClip.
+     */
+    static const unsigned int REPEAT_INDEFINITE = 0;
+
+    /**
+     * Defines an animation event listener.
+     */
+    class Listener
+    {
+        friend class AnimationClip;
+
+    public:
+
+        /*
+         * Constructor.
+         */
+        Listener() 
+        {
+        }
+
+        /**
+         * The type of animation event.
+         */
+        enum EventType 
+        {
+            /**
+             * Event fired when the clip begins.
+             */
+            BEGIN,
+
+            /**
+             * Event fired when the clip ends.
+             */
+            END,
+
+            /**
+             * Event fired at a specified time during a clip update.
+             */
+            TIME
+        };
+        
+        /*
+         * Destructor.
+         */
+        virtual ~Listener() { }
+
+        /**
+         * Handles when animation event occurs.
+         */
+        virtual void animationEvent(AnimationClip* clip, EventType type) = 0;
+    };
+
+    /**
+     * Gets the AnimationClip's ID.
+     *
+     * @return The AnimationClip's ID.
+     */
+    const char* getId() const;
+
+    /**
+     * Gets the Animation that this AnimationClip was created from.
+     * 
+     * @return The Animation that this clip was created from.
+     */
+    Animation* getAnimation() const;
+
+    /**
+     * Gets the AnimationClip's start time.
+     *
+     * @return The time (in milliseconds) that the AnimationClip starts playing from.
+     */
+    unsigned long getStartTime() const;
+
+    /**
+     * Gets the AnimationClip's end time.
+     * 
+     * @return The time (in milliseconds) that the AnimationClip will end.
+     */
+    unsigned long getEndTime() const;
+
+    /**
+     * Gets the AnimationClip's elapsed time.
+     *
+     * @return The elapsed time of the AnimationClip (in milliseconds).
+     */
+    float getElapsedTime() const;
+
+    /**
+     * Sets the AnimationClip's repeat count. Overrides repeat duration.
+     *
+     * Use REPEAT_INDEFINITE to play the AnimationClip indefinitely.
+     * 
+     * @param repeatCount The repeat count to set on the AnimationClip.
+     */
+    void setRepeatCount(float repeatCount);
+
+    /**
+     * Gets the AnimationClip's repeat count.
+     *
+     * @return The repeat count that is set on the AnimationClip.
+     */
+    float getRepeatCount() const;
+
+    /**
+     * Sets the AnimationClip's active duration. Overrides repeat count.
+     *
+     * Use REPEAT_INDEFINITE to play the AnimationClip indefinitely.
+     *
+     * @param duration The active duration that is set on the AnimationClip, in milliseconds.
+     */
+    void setActiveDuration(unsigned long duration);
+
+    /**
+     * Gets the AnimationClip's active duration.
+     * 
+     * @return the AnimationClip's active duration.
+     */
+    unsigned long getActiveDuration() const;
+
+    /**
+     * Gets the AnimationClip's duration.
+     *
+     * @return the AnimationClip's duration, in milliseconds.
+     */
+    unsigned long getDuration() const;
+
+    /**
+     * Set the AnimationClip's running speed. 
+     *
+     * @param speed The clips running speed.
+     */
+    void setSpeed(float speed);
+
+    /**
+     * Gets the AninimationClip's running speed.
+     *
+     * @return The AninimationClip's running speed.
+     */
+    float getSpeed() const;
+
+    /**
+     * Sets the blend weight of the AnimationClip.
+     *
+     * @param blendWeight The blend weight to apply to the clip.
+     */
+    void setBlendWeight(float blendWeight);
+
+    /** 
+     * Gets the blend weight of the AnimationClip.
+     *
+     * @return The blendweight of the AnimationClip.
+     */
+    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.
+     *
+     * @return true if the AnimationClip is playing; false if the AnimationClip is not playing.
+     */
+    bool isPlaying() const;
+
+    /**
+     * Plays the AnimationClip.
+     */
+    void play();
+
+    /**
+     * Stops the AnimationClip.
+     */
+    void stop();
+
+    /**
+     * Pauses the AnimationClip.
+     */
+    void pause();
+
+    /**
+     * Fades this clip out, and the specified clip in over the given duration.
+     *
+     * @param clip The clip to fade into.
+     * @param duration The duration of the fade.
+     */
+    void crossFade(AnimationClip* clip, unsigned long duration);
+
+    /**
+     * Adds an animation begin listener.
+     *
+     * @param listener The listener to be called when an AnimationClip begins.
+     */
+    void addBeginListener(AnimationClip::Listener* listener);
+
+    /**
+     * Adds an animation end listener.
+     *
+     * @param listener The listener to be called when an AnimationClip ends.
+     */
+    void addEndListener(AnimationClip::Listener* listener);
+
+    /**
+     * Adds an animation listener to be called back at the specified eventTime during the playback 
+     * of the AnimationClip.
+     *
+     * @param listener The listener to be called when the AnimationClip reaches the 
+     *      specified time in its playback.
+     * @param eventTime The time the listener will be called during the playback of the AnimationClip. 
+     *      Must be between 0 and the duration of the AnimationClip.
+     */
+    void addListener(AnimationClip::Listener* listener, unsigned long eventTime);
+
+    /**
+     * Adds an animation begin listener.
+     * 
+     * Note: the given Lua function must have the same function signature as AnimationClip::Listener::animationEvent.
+     *
+     * @param function The Lua script function to be called when an AnimationClip begins.
+     */
+    void addBeginListener(const char* function);
+
+    /**
+     * Adds an animation end listener.
+     * 
+     * Note: the given Lua function must have the same function signature as AnimationClip::Listener::animationEvent.
+     *
+     * @param function The Lua script function to be called when an AnimationClip ends.
+     */
+    void addEndListener(const char* function);
+
+    /**
+     * Adds an animation listener to be called back at the specified eventTime during the playback 
+     * of the AnimationClip.
+     * 
+     * Note: the given Lua function must have the same function signature as AnimationClip::Listener::animationEvent.
+     * 
+     * @param function The Lua script function to be called when an AnimationClip reaches the 
+     *      specified time in its playback.
+     * @param eventTime The time the listener will be called during the playback of the AnimationClip. 
+     *      Must be between 0 and the duration of the AnimationClip.
+     */
+    void addListener(const char* function, unsigned long eventTime);
+
+private:
+    
+    static const unsigned char CLIP_IS_PLAYING_BIT = 0x01;             // Bit representing whether AnimationClip is a running clip in AnimationController
+    static const unsigned char CLIP_IS_STARTED_BIT = 0x02;             // Bit representing whether the AnimationClip has actually been started (ie: received first call to update())
+    static const unsigned char CLIP_IS_FADING_OUT_STARTED_BIT = 0x04;  // Bit representing that a cross fade has started.
+    static const unsigned char CLIP_IS_FADING_OUT_BIT = 0x08;          // Bit representing whether the clip is fading out.
+    static const unsigned char CLIP_IS_FADING_IN_BIT = 0x10;           // Bit representing whether the clip is fading out.
+    static const unsigned char CLIP_IS_MARKED_FOR_REMOVAL_BIT = 0x20;  // Bit representing whether the clip has ended and should be removed from the AnimationController.
+    static const unsigned char CLIP_IS_RESTARTED_BIT = 0x40;           // Bit representing if the clip should be restarted by the AnimationController.
+    static const unsigned char CLIP_IS_PAUSED_BIT = 0x80;              // Bit representing if the clip is currently paused.
+    static const unsigned char CLIP_ALL_BITS = 0xFF;                   // Bit mask for all the state bits.
+
+    /**
+     * ListenerEvent.
+     *
+     * Internal structure used for storing the event time at which an AnimationClip::Listener should be called back.
+     */
+    struct ListenerEvent
+    {
+        /** 
+         * Constructor.
+         */
+        ListenerEvent(Listener* listener, unsigned long eventTime);
+
+        /**
+         * Destructor.
+         */
+        ~ListenerEvent();
+
+        /**
+         * Hidden copy assignment operator.
+         */
+        ListenerEvent& operator=(const ListenerEvent&);
+
+        Listener* _listener;        // This listener to call back when this event is triggered.
+        unsigned long _eventTime;   // The time at which the listener will be called back at during the playback of the AnimationClip.
+    };
+
+    /**
+     * Listener implementation for script callbacks.
+     */
+    struct ScriptListener : public AnimationClip::Listener
+    {
+        /**
+         * Constructor.
+         */
+        ScriptListener(const std::string& function);
+
+        /**
+         * @see AnimationClip::Listener::animationEvent
+         */
+        void animationEvent(AnimationClip* clip, EventType type);
+
+        /** The function to call back when an animation event occurs. */
+        std::string function;
+    };
+
+    /**
+     * Constructor.
+     */
+    AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime);
+
+    /**
+     * Constructor.
+     */
+    AnimationClip();
+
+    /**
+     * Constructor.
+     */
+    AnimationClip(const AnimationClip& copy);
+
+    /**
+     * Destructor.
+     */
+    ~AnimationClip();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    AnimationClip& operator=(const AnimationClip&);
+
+    /**
+     * Updates the animation with the elapsed time.
+     */
+    bool update(float elapsedTime);
+
+    /**
+     * Handles when the AnimationClip begins.
+     */
+    void onBegin();
+
+    /**
+     * Handles when the AnimationClip ends.
+     */
+    void onEnd();
+
+    /**
+     * Determines whether the given bit is set in the AnimationClip's state.
+     */
+    bool isClipStateBitSet(unsigned char bit) const;
+
+    /**
+     * Sets the given bit in the AnimationClip's state.
+     */
+    void setClipStateBit(unsigned char bit);
+
+    /**
+     * Resets the given bit in the AnimationClip's state.
+     */
+    void resetClipStateBit(unsigned char bit);
+
+    /**
+     * Clones the animation clip.
+     * 
+     * @param animation The animation that the new clip belongs to.
+     * 
+     * @return The newly created animation clip.
+     */
+    AnimationClip* clone(Animation* animation) const;
+
+    std::string _id;                                    // AnimationClip ID.
+    Animation* _animation;                              // The Animation this clip is created from.
+    unsigned long _startTime;                           // Start time of the clip.
+    unsigned long _endTime;                             // End time of the clip.
+    unsigned long _duration;                            // The total duration.
+    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.
+    float _elapsedTime;                                 // Time elapsed while the clip is running.
+    AnimationClip* _crossFadeToClip;                    // The clip to cross fade to.
+    float _crossFadeOutElapsed;                         // The amount of time that has elapsed for the crossfade.
+    unsigned long _crossFadeOutDuration;                // The duration of the cross fade.
+    float _blendWeight;                                 // The clip's blendweight.
+    std::vector<AnimationValue*> _values;               // AnimationValue holder.
+    std::vector<Listener*>* _beginListeners;            // Collection of begin listeners on the clip.
+    std::vector<Listener*>* _endListeners;              // Collection of end listeners on the clip.
+    std::list<ListenerEvent*>* _listeners;              // Ordered collection of listeners on the clip.
+    std::list<ListenerEvent*>::iterator* _listenerItr;  // Iterator that points to the next listener event to be triggered.
+    std::vector<ScriptListener*>* _scriptListeners;     // Collection of listeners that are bound to Lua script functions.
+};
+
+}
+
+#endif

+ 136 - 136
gameplay/src/AnimationController.cpp

@@ -1,136 +1,136 @@
-#include "Base.h"
-#include "AnimationController.h"
-#include "Game.h"
-#include "Curve.h"
-
-namespace gameplay
-{
-
-AnimationController::AnimationController()
-    : _state(STOPPED)
-{
-}
-
-AnimationController::~AnimationController()
-{
-}
-
-void AnimationController::stopAllAnimations() 
-{
-    std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
-    while (clipIter != _runningClips.end())
-    {
-        AnimationClip* clip = *clipIter;
-        GP_ASSERT(clip);
-        clip->stop();
-        clipIter++;
-    }
-}
-
-AnimationController::State AnimationController::getState() const
-{
-    return _state;
-}
-
-void AnimationController::initialize()
-{
-    _state = IDLE;
-}
-
-void AnimationController::finalize()
-{
-    std::list<AnimationClip*>::iterator itr = _runningClips.begin();
-    for ( ; itr != _runningClips.end(); itr++)
-    {
-        AnimationClip* clip = *itr;
-        SAFE_RELEASE(clip);
-    }
-    _runningClips.clear();
-    _state = STOPPED;
-}
-
-void AnimationController::resume()
-{
-    if (_runningClips.empty())
-        _state = IDLE;
-    else
-        _state = RUNNING;
-}
-
-void AnimationController::pause()
-{
-    _state = PAUSED;
-}
-
-void AnimationController::schedule(AnimationClip* clip)
-{
-    if (_runningClips.empty())
-    {
-        _state = RUNNING;
-    }
-
-    GP_ASSERT(clip);
-    clip->addRef();
-    _runningClips.push_back(clip);
-}
-
-void AnimationController::unschedule(AnimationClip* clip)
-{
-    std::list<AnimationClip*>::iterator clipItr = _runningClips.begin();
-    while (clipItr != _runningClips.end())
-    {
-        AnimationClip* rClip = (*clipItr);
-        if (rClip == clip)
-        {
-            _runningClips.erase(clipItr);
-            SAFE_RELEASE(clip);
-            break;
-        }
-        clipItr++;
-    }
-
-    if (_runningClips.empty())
-        _state = IDLE;
-}
-
-void AnimationController::update(float elapsedTime)
-{
-    if (_state != RUNNING)
-        return;
-    
-    Transform::suspendTransformChanged();
-
-    // Loop through running clips and call update() on them.
-    std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
-    while (clipIter != _runningClips.end())
-    {
-        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.
-            clip->onEnd();
-            clip->setClipStateBit(AnimationClip::CLIP_IS_PLAYING_BIT);
-            _runningClips.push_back(clip);
-            clipIter = _runningClips.erase(clipIter);
-        }
-        else if (clip->update(elapsedTime))
-        {
-            clip->release();
-            clipIter = _runningClips.erase(clipIter);
-        }
-        else
-        {
-            clipIter++;
-        }
-        clip->release();
-    }
-
-    Transform::resumeTransformChanged();
-
-    if (_runningClips.empty())
-        _state = IDLE;
-}
-
-}
+#include "Base.h"
+#include "AnimationController.h"
+#include "Game.h"
+#include "Curve.h"
+
+namespace gameplay
+{
+
+AnimationController::AnimationController()
+    : _state(STOPPED)
+{
+}
+
+AnimationController::~AnimationController()
+{
+}
+
+void AnimationController::stopAllAnimations() 
+{
+    std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
+    while (clipIter != _runningClips.end())
+    {
+        AnimationClip* clip = *clipIter;
+        GP_ASSERT(clip);
+        clip->stop();
+        clipIter++;
+    }
+}
+
+AnimationController::State AnimationController::getState() const
+{
+    return _state;
+}
+
+void AnimationController::initialize()
+{
+    _state = IDLE;
+}
+
+void AnimationController::finalize()
+{
+    std::list<AnimationClip*>::iterator itr = _runningClips.begin();
+    for ( ; itr != _runningClips.end(); itr++)
+    {
+        AnimationClip* clip = *itr;
+        SAFE_RELEASE(clip);
+    }
+    _runningClips.clear();
+    _state = STOPPED;
+}
+
+void AnimationController::resume()
+{
+    if (_runningClips.empty())
+        _state = IDLE;
+    else
+        _state = RUNNING;
+}
+
+void AnimationController::pause()
+{
+    _state = PAUSED;
+}
+
+void AnimationController::schedule(AnimationClip* clip)
+{
+    if (_runningClips.empty())
+    {
+        _state = RUNNING;
+    }
+
+    GP_ASSERT(clip);
+    clip->addRef();
+    _runningClips.push_back(clip);
+}
+
+void AnimationController::unschedule(AnimationClip* clip)
+{
+    std::list<AnimationClip*>::iterator clipItr = _runningClips.begin();
+    while (clipItr != _runningClips.end())
+    {
+        AnimationClip* rClip = (*clipItr);
+        if (rClip == clip)
+        {
+            _runningClips.erase(clipItr);
+            SAFE_RELEASE(clip);
+            break;
+        }
+        clipItr++;
+    }
+
+    if (_runningClips.empty())
+        _state = IDLE;
+}
+
+void AnimationController::update(float elapsedTime)
+{
+    if (_state != RUNNING)
+        return;
+    
+    Transform::suspendTransformChanged();
+
+    // Loop through running clips and call update() on them.
+    std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
+    while (clipIter != _runningClips.end())
+    {
+        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.
+            clip->onEnd();
+            clip->setClipStateBit(AnimationClip::CLIP_IS_PLAYING_BIT);
+            _runningClips.push_back(clip);
+            clipIter = _runningClips.erase(clipIter);
+        }
+        else if (clip->update(elapsedTime))
+        {
+            clip->release();
+            clipIter = _runningClips.erase(clipIter);
+        }
+        else
+        {
+            clipIter++;
+        }
+        clip->release();
+    }
+
+    Transform::resumeTransformChanged();
+
+    if (_runningClips.empty())
+        _state = IDLE;
+}
+
+}

+ 106 - 106
gameplay/src/AnimationController.h

@@ -1,106 +1,106 @@
-#ifndef ANIMATIONCONTROLLER_H_
-#define ANIMATIONCONTROLLER_H_
-
-#include "AnimationClip.h"
-#include "Animation.h"
-#include "AnimationTarget.h"
-#include "Properties.h"
-
-namespace gameplay
-{
-
-/**
- * Defines a class for controlling game animation.
- */
-class AnimationController
-{
-    friend class Game;
-    friend class Animation;
-    friend class AnimationClip;
-    friend class SceneLoader;
-
-public:
-
-    /** 
-     * Stops all AnimationClips currently playing on the AnimationController.
-     */
-    void stopAllAnimations();
-       
-private:
-
-    /**
-     * The states that the AnimationController may be in.
-     */
-    enum State
-    {
-        RUNNING,
-        IDLE,
-        PAUSED,
-        STOPPED
-    };
-
-    /**
-     * Constructor.
-     */
-    AnimationController();
-
-    /**
-     * Constructor.
-     */
-    AnimationController(const AnimationController& copy);
-
-    /**
-     * Destructor.
-     */
-    ~AnimationController();
-
-    /**
-     * Gets the controller's state.
-     *
-     * @return The current state.
-     */
-    State getState() const;
-
-    /**
-     * Callback for when the controller is initialized.
-     */
-    void initialize();
-
-    /*
-     * Callback for when the controller is finalized.
-     */
-    void finalize();
-
-    /**
-     * Resumes the AnimationController.
-     */
-    void resume();
-    
-    /**
-     * Pauses the AnimationController.
-     */
-    void pause();
-
-    /**
-     * Schedules an AnimationClip to run.
-     */
-    void schedule(AnimationClip* clip);
-
-    /**
-     * Unschedules an AnimationClip.
-     */
-    void unschedule(AnimationClip* clip);
-    
-    /**
-     * Callback for when the controller receives a frame update event.
-     */
-    void update(float elapsedTime);
-    
-    State _state;                                 // The current state of the AnimationController.
-    std::list<AnimationClip*> _runningClips;      // A list of running AnimationClips.
-};
-
-}
-
-#endif
-
+#ifndef ANIMATIONCONTROLLER_H_
+#define ANIMATIONCONTROLLER_H_
+
+#include "AnimationClip.h"
+#include "Animation.h"
+#include "AnimationTarget.h"
+#include "Properties.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a class for controlling game animation.
+ */
+class AnimationController
+{
+    friend class Game;
+    friend class Animation;
+    friend class AnimationClip;
+    friend class SceneLoader;
+
+public:
+
+    /** 
+     * Stops all AnimationClips currently playing on the AnimationController.
+     */
+    void stopAllAnimations();
+       
+private:
+
+    /**
+     * The states that the AnimationController may be in.
+     */
+    enum State
+    {
+        RUNNING,
+        IDLE,
+        PAUSED,
+        STOPPED
+    };
+
+    /**
+     * Constructor.
+     */
+    AnimationController();
+
+    /**
+     * Constructor.
+     */
+    AnimationController(const AnimationController& copy);
+
+    /**
+     * Destructor.
+     */
+    ~AnimationController();
+
+    /**
+     * Gets the controller's state.
+     *
+     * @return The current state.
+     */
+    State getState() const;
+
+    /**
+     * Callback for when the controller is initialized.
+     */
+    void initialize();
+
+    /*
+     * Callback for when the controller is finalized.
+     */
+    void finalize();
+
+    /**
+     * Resumes the AnimationController.
+     */
+    void resume();
+    
+    /**
+     * Pauses the AnimationController.
+     */
+    void pause();
+
+    /**
+     * Schedules an AnimationClip to run.
+     */
+    void schedule(AnimationClip* clip);
+
+    /**
+     * Unschedules an AnimationClip.
+     */
+    void unschedule(AnimationClip* clip);
+    
+    /**
+     * Callback for when the controller receives a frame update event.
+     */
+    void update(float elapsedTime);
+    
+    State _state;                                 // The current state of the AnimationController.
+    std::list<AnimationClip*> _runningClips;      // A list of running AnimationClips.
+};
+
+}
+
+#endif
+

+ 560 - 560
gameplay/src/AnimationTarget.cpp

@@ -1,560 +1,560 @@
-#include "Base.h"
-#include "AnimationTarget.h"
-#include "Animation.h"
-#include "Game.h"
-#include "Node.h"
-
-#define ANIMATION_TARGET_INDEFINITE_STR "INDEFINITE"
-
-namespace gameplay
-{
-
-AnimationTarget::AnimationTarget()
-    : _targetType(SCALAR), _animationChannels(NULL)
-{
-}
-
-AnimationTarget::~AnimationTarget()
-{
-    if (_animationChannels)
-    {
-        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
-        while (itr != _animationChannels->end())
-        {
-            Animation::Channel* channel = (*itr);
-            GP_ASSERT(channel);
-            GP_ASSERT(channel->_animation);
-            channel->_animation->removeChannel(channel);
-            SAFE_DELETE(channel);
-            itr++;
-        }
-        _animationChannels->clear();
-        SAFE_DELETE(_animationChannels);
-    }
-}
-
-Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, Curve::InterpolationType type)
-{
-    GP_ASSERT(type != Curve::BEZIER && type != Curve::HERMITE);
-    GP_ASSERT(keyCount >= 1 && keyTimes && keyValues);
-
-    Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, type);
-
-    return animation;
-}
-
-Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type)
-{
-    GP_ASSERT(keyCount >= 1 && keyTimes && keyValues && keyInValue && keyOutValue);
-    Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
-
-    return animation;
-}
-
-Animation* AnimationTarget::createAnimation(const char* id, const char* url)
-{
-    Properties* p = Properties::create(url);
-    GP_ASSERT(p);
-
-    Animation* animation = createAnimation(id, (strlen(p->getNamespace()) > 0) ? p : p->getNextNamespace());
-
-    SAFE_DELETE(p);
-
-    return animation;
-}
-
-Animation* AnimationTarget::createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration)
-{
-    GP_ASSERT(from);
-    GP_ASSERT(to);
-
-    const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
-    GP_ASSERT(propertyComponentCount > 0);
-    float* keyValues = new float[2 * propertyComponentCount];
-
-    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
-    memcpy(keyValues + propertyComponentCount, to, sizeof(float) * propertyComponentCount);
-
-    unsigned int* keyTimes = new unsigned int[2];
-    keyTimes[0] = 0;
-    keyTimes[1] = (unsigned int)duration;
-
-    Animation* animation = createAnimation(id, propertyId, 2, keyTimes, keyValues, type);
-
-    SAFE_DELETE_ARRAY(keyValues);
-    SAFE_DELETE_ARRAY(keyTimes);
-
-    return animation;
-}
-
-Animation* AnimationTarget::createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration)
-{
-    GP_ASSERT(from);
-    GP_ASSERT(by);
-
-    const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
-    GP_ASSERT(propertyComponentCount > 0);
-    float* keyValues = new float[2 * propertyComponentCount];
-
-    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
-
-    convertByValues(propertyId, propertyComponentCount, from, by);
-    memcpy(keyValues + propertyComponentCount, by, sizeof(float) * propertyComponentCount);
-
-    unsigned int* keyTimes = new unsigned int[2];
-    keyTimes[0] = 0;
-    keyTimes[1] = (unsigned int)duration;
-
-    Animation* animation = createAnimation(id, propertyId, 2, keyTimes, keyValues, type);
-
-    SAFE_DELETE_ARRAY(keyValues);
-    SAFE_DELETE_ARRAY(keyTimes);
-
-    return animation;
-}
-
-Animation* AnimationTarget::createAnimation(const char* id, Properties* animationProperties)
-{
-    GP_ASSERT(animationProperties);
-    if (std::strcmp(animationProperties->getNamespace(), "animation") != 0)
-    {
-        GP_ERROR("Invalid animation namespace '%s'.", animationProperties->getNamespace());
-        return NULL;
-    }
-
-    const char* propertyIdStr = animationProperties->getString("property");
-    if (propertyIdStr == NULL)
-    {
-        GP_ERROR("Attribute 'property' must be specified for an animation.");
-        return NULL;
-    }
-
-    // Get animation target property id
-    int propertyId = AnimationTarget::getPropertyId(_targetType, propertyIdStr);
-    if (propertyId == -1)
-    {
-        GP_ERROR("Property ID is invalid.");
-        return NULL;
-    }
-
-    unsigned int keyCount = animationProperties->getInt("keyCount");
-    if (keyCount == 0)
-    {
-        GP_ERROR("Attribute 'keyCount' must be specified for an animation.");
-        return NULL;
-    }
-
-    const char* keyTimesStr = animationProperties->getString("keyTimes");
-    if (keyTimesStr == NULL)
-    {
-        GP_ERROR("Attribute 'keyTimes' must be specified for an animation.");
-        return NULL;
-    }
-
-    const char* keyValuesStr = animationProperties->getString("keyValues");
-    if (keyValuesStr == NULL)
-    {
-        GP_ERROR("Attribute 'keyValues' must be specified for an animation.");
-        return NULL;
-    }
-
-    const char* curveStr = animationProperties->getString("curve");
-    if (curveStr == NULL)
-    {
-        GP_ERROR("Attribute 'curve' must be specified for an animation.");
-        return NULL;
-    }
-
-    char delimeter = ' ';
-    size_t startOffset = 0;
-    size_t endOffset = std::string::npos;
-
-    unsigned int* keyTimes = new unsigned int[keyCount];
-    for (size_t i = 0; i < keyCount; i++)
-    {
-        endOffset = static_cast<std::string>(keyTimesStr).find_first_of(delimeter, startOffset);
-        if (endOffset != std::string::npos)
-        {
-            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, endOffset - startOffset).c_str(), NULL, 0);
-        }
-        else
-        {
-            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, static_cast<std::string>(keyTimesStr).length()).c_str(), NULL, 0);
-        }
-        startOffset = endOffset + 1;
-    }
-
-    startOffset = 0;
-    endOffset = std::string::npos;
-
-    int componentCount = getAnimationPropertyComponentCount(propertyId);
-    GP_ASSERT(componentCount > 0);
-
-    unsigned int components = keyCount * componentCount;
-
-    float* keyValues = new float[components];
-    for (unsigned int i = 0; i < components; i++)
-    {
-        endOffset = static_cast<std::string>(keyValuesStr).find_first_of(delimeter, startOffset);
-        if (endOffset != std::string::npos)
-        {
-            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, endOffset - startOffset).c_str());
-        }
-        else
-        {
-            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, static_cast<std::string>(keyValuesStr).length()).c_str());
-        }
-        startOffset = endOffset + 1;
-    }
-
-    const char* keyInStr = animationProperties->getString("keyIn");
-    float* keyIn = NULL;
-    if (keyInStr)
-    {
-        keyIn = new float[components];
-        startOffset = 0;
-        endOffset = std::string::npos;
-        for (unsigned int i = 0; i < components; i++)
-        {
-            endOffset = static_cast<std::string>(keyInStr).find_first_of(delimeter, startOffset);
-            if (endOffset != std::string::npos)
-            {
-                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, endOffset - startOffset).c_str());
-            }
-            else
-            {
-                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, static_cast<std::string>(keyInStr).length()).c_str());
-            }
-            startOffset = endOffset + 1;
-        }
-    }
-
-    const char* keyOutStr = animationProperties->getString("keyOut");
-    float* keyOut = NULL;
-    if (keyOutStr)
-    {
-        keyOut = new float[components];
-        startOffset = 0;
-        endOffset = std::string::npos;
-        for (unsigned int i = 0; i < components; i++)
-        {
-            endOffset = static_cast<std::string>(keyOutStr).find_first_of(delimeter, startOffset);
-            if (endOffset != std::string::npos)
-            {
-                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, endOffset - startOffset).c_str());
-            }
-            else
-            {
-                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, static_cast<std::string>(keyOutStr).length()).c_str());
-            }
-            startOffset = endOffset + 1;
-        }
-    }
-
-    int curve = Curve::getInterpolationType(curveStr);
-
-    Animation* animation = NULL;
-    if (keyIn && keyOut)
-    {
-        animation = createAnimation(id, propertyId, keyCount, keyTimes, keyValues, keyIn, keyOut, (Curve::InterpolationType)curve);
-    }
-    else
-    {
-        animation = createAnimation(id, propertyId, keyCount, keyTimes, keyValues, (Curve::InterpolationType) curve);
-    }
-
-    const char* repeat = animationProperties->getString("repeatCount");
-    if (repeat)
-    {
-        if (strcmp(repeat, ANIMATION_TARGET_INDEFINITE_STR) == 0)
-        {
-            animation->getClip()->setRepeatCount(AnimationClip::REPEAT_INDEFINITE);
-        }
-        else
-        {
-            float value;
-            sscanf(repeat, "%f", &value);
-            animation->getClip()->setRepeatCount(value);
-        }
-    }
-    
-    SAFE_DELETE_ARRAY(keyOut);
-    SAFE_DELETE_ARRAY(keyIn);
-    SAFE_DELETE_ARRAY(keyValues);
-    SAFE_DELETE_ARRAY(keyTimes);
-
-    Properties* pClip = animationProperties->getNextNamespace();
-    if (pClip && std::strcmp(pClip->getNamespace(), "clip") == 0)
-    {
-        int frameCount = animationProperties->getInt("frameCount");
-        if (frameCount <= 0)
-        {
-            GP_ERROR("Frame count must be greater than zero for a clip.");
-            return animation;
-        }
-        animation->createClips(animationProperties, (unsigned int) frameCount);
-    }
-    
-    return animation;
-}
-
-void AnimationTarget::destroyAnimation(const char* id)
-{
-    // Find the animation with the specified ID.
-    Animation::Channel* channel = getChannel(id);
-    if (channel == NULL)
-        return;
-
-    // Remove this target's channel from animation, and from the target's list of channels.
-    GP_ASSERT(channel->_animation);
-    channel->_animation->removeChannel(channel);
-    removeChannel(channel);
-
-    SAFE_DELETE(channel);
-}
-
-Animation* AnimationTarget::getAnimation(const char* id) const
-{
-    if (_animationChannels)
-    {
-        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
-        GP_ASSERT(*itr);
-
-        if (id == NULL)
-            return (*itr)->_animation;
-
-        Animation::Channel* channel = NULL;
-        for (; itr != _animationChannels->end(); itr++)
-        {
-            channel = (Animation::Channel*)(*itr);
-            GP_ASSERT(channel);
-            GP_ASSERT(channel->_animation);
-            if (channel->_animation->_id.compare(id) == 0)
-            {
-                return channel->_animation;
-            }
-        }
-    }
-
-    return NULL;
-}
-
-int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
-{
-    GP_ASSERT(propertyIdStr);
-
-    if (type == AnimationTarget::TRANSFORM)
-    {
-        if (strcmp(propertyIdStr, "ANIMATE_SCALE") == 0)
-        {
-            return Transform::ANIMATE_SCALE;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_X") == 0)
-        {
-            return Transform::ANIMATE_SCALE_X;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_Y") == 0)
-        {
-            return Transform::ANIMATE_SCALE_Y;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_Z") == 0)
-        {
-            return Transform::ANIMATE_SCALE_Z;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_ROTATE") == 0)
-        {
-            return Transform::ANIMATE_ROTATE;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE") == 0)
-        {
-            return Transform::ANIMATE_TRANSLATE;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE_X") == 0)
-        {
-            return Transform::ANIMATE_TRANSLATE_X;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE_Y") == 0)
-        {
-            return Transform::ANIMATE_TRANSLATE_Y;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE_Z") == 0)
-        {
-            return Transform::ANIMATE_TRANSLATE_Z;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_ROTATE_TRANSLATE") == 0)
-        {
-            return Transform::ANIMATE_ROTATE_TRANSLATE;
-        }
-        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_ROTATE_TRANSLATE") == 0)
-        {
-            return Transform::ANIMATE_SCALE_ROTATE_TRANSLATE;
-        }
-    }
-    else
-    {
-        if (strcmp(propertyIdStr, "ANIMATE_UNIFORM") == 0)
-        {
-            return MaterialParameter::ANIMATE_UNIFORM;
-        }
-    }
-
-    return -1;
-}
-
-void AnimationTarget::addChannel(Animation::Channel* channel)
-{
-    if (_animationChannels == NULL)
-        _animationChannels = new std::vector<Animation::Channel*>;
-
-    GP_ASSERT(channel);
-    _animationChannels->push_back(channel);
-}
-
-void AnimationTarget::removeChannel(Animation::Channel* channel)
-{
-    if (_animationChannels)
-    {
-        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
-        for ( ; itr != _animationChannels->end(); itr++)
-        {
-            Animation::Channel* temp = *itr;
-            if (channel == temp)
-            {
-                _animationChannels->erase(itr);
-
-                if (_animationChannels->empty())
-                    SAFE_DELETE(_animationChannels);
-
-                return;
-            }
-        }
-    }
-}
-
-Animation::Channel* AnimationTarget::getChannel(const char* id) const
-{
-    if (_animationChannels)
-    {
-        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
-
-        if (id == NULL)
-            return (*itr);
-
-        Animation::Channel* channel = NULL;
-        for (; itr != _animationChannels->end(); itr++)
-        {
-            channel = (Animation::Channel*)(*itr);
-            GP_ASSERT(channel);
-            if (channel->_animation->_id.compare(id) == 0)
-            {
-                return channel;
-            }
-        }
-    }
-
-    return NULL;
-}
-
-void AnimationTarget::cloneInto(AnimationTarget* target, NodeCloneContext &context) const
-{
-    if (_animationChannels)
-    {
-        for (std::vector<Animation::Channel*>::const_iterator it = _animationChannels->begin(); it != _animationChannels->end(); ++it)
-        {
-            Animation::Channel* channel = *it;
-            GP_ASSERT(channel);
-            GP_ASSERT(channel->_animation);
-
-            Animation* animation = context.findClonedAnimation(channel->_animation);
-            if (animation != NULL)
-            {
-                Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
-                animation->addChannel(channelCopy);
-            }
-            else
-            {
-                // Clone the animation and register it with the context so that it only gets cloned once.
-                animation = channel->_animation->clone(channel, target);
-                context.registerClonedAnimation(channel->_animation, animation);
-            }
-        }
-    }
-}
-
-void AnimationTarget::convertByValues(unsigned int propertyId, unsigned int componentCount, float* from, float* by)
-{
-    if (_targetType == AnimationTarget::TRANSFORM)
-    {
-        switch(propertyId)
-        {
-            case Transform::ANIMATE_SCALE:
-            case Transform::ANIMATE_SCALE_UNIT:
-            case Transform::ANIMATE_SCALE_X:
-            case Transform::ANIMATE_SCALE_Y:
-            case Transform::ANIMATE_SCALE_Z:
-            {
-                convertScaleByValues(from, by, componentCount);
-                break;
-            }
-            case Transform::ANIMATE_TRANSLATE:
-            case Transform::ANIMATE_TRANSLATE_X:
-            case Transform::ANIMATE_TRANSLATE_Y:
-            case Transform::ANIMATE_TRANSLATE_Z:
-            {
-                convertByValues(from, by, componentCount);
-                break;
-            }
-            case Transform::ANIMATE_ROTATE:
-            {
-                convertQuaternionByValues(from, by);
-                break;
-            }
-            case Transform::ANIMATE_ROTATE_TRANSLATE:
-            {
-                convertQuaternionByValues(from, by);
-                convertByValues(from + 4, by + 4, 3);
-                break;
-            }
-            case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
-            {
-                convertScaleByValues(from, by, 3);
-                convertQuaternionByValues(from + 3, by + 3);
-                convertByValues(from + 7, by + 7, 3);
-                break;
-            }
-        }
-    }
-    else
-    {
-        convertByValues(from, by, componentCount);
-    }
-}
-
-void AnimationTarget::convertQuaternionByValues(float* from, float* by)
-{
-    Quaternion qFrom(from);
-    Quaternion qBy(by);
-    qBy.multiply(qFrom);
-    memcpy(by, (float*)&qBy, sizeof(float) * 4);
-}
-
-void AnimationTarget::convertScaleByValues(float* from, float* by, unsigned int componentCount)
-{
-    for (unsigned int i = 0; i < componentCount; i++)
-    {
-        by[i] *= from[i];
-    }
-}
-
-void AnimationTarget::convertByValues(float* from, float* by, unsigned int componentCount)
-{
-    for (unsigned int i = 0; i < componentCount; i++)
-    {
-        by[i] += from[i];
-    }
-}
-
-}
-
-
-
+#include "Base.h"
+#include "AnimationTarget.h"
+#include "Animation.h"
+#include "Game.h"
+#include "Node.h"
+
+#define ANIMATION_TARGET_INDEFINITE_STR "INDEFINITE"
+
+namespace gameplay
+{
+
+AnimationTarget::AnimationTarget()
+    : _targetType(SCALAR), _animationChannels(NULL)
+{
+}
+
+AnimationTarget::~AnimationTarget()
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+        while (itr != _animationChannels->end())
+        {
+            Animation::Channel* channel = (*itr);
+            GP_ASSERT(channel);
+            GP_ASSERT(channel->_animation);
+            channel->_animation->removeChannel(channel);
+            SAFE_DELETE(channel);
+            itr++;
+        }
+        _animationChannels->clear();
+        SAFE_DELETE(_animationChannels);
+    }
+}
+
+Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, Curve::InterpolationType type)
+{
+    GP_ASSERT(type != Curve::BEZIER && type != Curve::HERMITE);
+    GP_ASSERT(keyCount >= 1 && keyTimes && keyValues);
+
+    Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, type);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type)
+{
+    GP_ASSERT(keyCount >= 1 && keyTimes && keyValues && keyInValue && keyOutValue);
+    Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimation(const char* id, const char* url)
+{
+    Properties* p = Properties::create(url);
+    GP_ASSERT(p);
+
+    Animation* animation = createAnimation(id, (strlen(p->getNamespace()) > 0) ? p : p->getNextNamespace());
+
+    SAFE_DELETE(p);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration)
+{
+    GP_ASSERT(from);
+    GP_ASSERT(to);
+
+    const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
+    GP_ASSERT(propertyComponentCount > 0);
+    float* keyValues = new float[2 * propertyComponentCount];
+
+    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
+    memcpy(keyValues + propertyComponentCount, to, sizeof(float) * propertyComponentCount);
+
+    unsigned int* keyTimes = new unsigned int[2];
+    keyTimes[0] = 0;
+    keyTimes[1] = (unsigned int)duration;
+
+    Animation* animation = createAnimation(id, propertyId, 2, keyTimes, keyValues, type);
+
+    SAFE_DELETE_ARRAY(keyValues);
+    SAFE_DELETE_ARRAY(keyTimes);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration)
+{
+    GP_ASSERT(from);
+    GP_ASSERT(by);
+
+    const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
+    GP_ASSERT(propertyComponentCount > 0);
+    float* keyValues = new float[2 * propertyComponentCount];
+
+    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
+
+    convertByValues(propertyId, propertyComponentCount, from, by);
+    memcpy(keyValues + propertyComponentCount, by, sizeof(float) * propertyComponentCount);
+
+    unsigned int* keyTimes = new unsigned int[2];
+    keyTimes[0] = 0;
+    keyTimes[1] = (unsigned int)duration;
+
+    Animation* animation = createAnimation(id, propertyId, 2, keyTimes, keyValues, type);
+
+    SAFE_DELETE_ARRAY(keyValues);
+    SAFE_DELETE_ARRAY(keyTimes);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimation(const char* id, Properties* animationProperties)
+{
+    GP_ASSERT(animationProperties);
+    if (std::strcmp(animationProperties->getNamespace(), "animation") != 0)
+    {
+        GP_ERROR("Invalid animation namespace '%s'.", animationProperties->getNamespace());
+        return NULL;
+    }
+
+    const char* propertyIdStr = animationProperties->getString("property");
+    if (propertyIdStr == NULL)
+    {
+        GP_ERROR("Attribute 'property' must be specified for an animation.");
+        return NULL;
+    }
+
+    // Get animation target property id
+	int propertyId = getPropertyId(_targetType, propertyIdStr);
+    if (propertyId == -1)
+    {
+        GP_ERROR("Property ID is invalid.");
+        return NULL;
+    }
+
+    unsigned int keyCount = animationProperties->getInt("keyCount");
+    if (keyCount == 0)
+    {
+        GP_ERROR("Attribute 'keyCount' must be specified for an animation.");
+        return NULL;
+    }
+
+    const char* keyTimesStr = animationProperties->getString("keyTimes");
+    if (keyTimesStr == NULL)
+    {
+        GP_ERROR("Attribute 'keyTimes' must be specified for an animation.");
+        return NULL;
+    }
+
+    const char* keyValuesStr = animationProperties->getString("keyValues");
+    if (keyValuesStr == NULL)
+    {
+        GP_ERROR("Attribute 'keyValues' must be specified for an animation.");
+        return NULL;
+    }
+
+    const char* curveStr = animationProperties->getString("curve");
+    if (curveStr == NULL)
+    {
+        GP_ERROR("Attribute 'curve' must be specified for an animation.");
+        return NULL;
+    }
+
+    char delimeter = ' ';
+    size_t startOffset = 0;
+    size_t endOffset = std::string::npos;
+
+    unsigned int* keyTimes = new unsigned int[keyCount];
+    for (size_t i = 0; i < keyCount; i++)
+    {
+        endOffset = static_cast<std::string>(keyTimesStr).find_first_of(delimeter, startOffset);
+        if (endOffset != std::string::npos)
+        {
+            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, endOffset - startOffset).c_str(), NULL, 0);
+        }
+        else
+        {
+            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, static_cast<std::string>(keyTimesStr).length()).c_str(), NULL, 0);
+        }
+        startOffset = endOffset + 1;
+    }
+
+    startOffset = 0;
+    endOffset = std::string::npos;
+
+    int componentCount = getAnimationPropertyComponentCount(propertyId);
+    GP_ASSERT(componentCount > 0);
+
+    unsigned int components = keyCount * componentCount;
+
+    float* keyValues = new float[components];
+    for (unsigned int i = 0; i < components; i++)
+    {
+        endOffset = static_cast<std::string>(keyValuesStr).find_first_of(delimeter, startOffset);
+        if (endOffset != std::string::npos)
+        {
+            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, endOffset - startOffset).c_str());
+        }
+        else
+        {
+            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, static_cast<std::string>(keyValuesStr).length()).c_str());
+        }
+        startOffset = endOffset + 1;
+    }
+
+    const char* keyInStr = animationProperties->getString("keyIn");
+    float* keyIn = NULL;
+    if (keyInStr)
+    {
+        keyIn = new float[components];
+        startOffset = 0;
+        endOffset = std::string::npos;
+        for (unsigned int i = 0; i < components; i++)
+        {
+            endOffset = static_cast<std::string>(keyInStr).find_first_of(delimeter, startOffset);
+            if (endOffset != std::string::npos)
+            {
+                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, endOffset - startOffset).c_str());
+            }
+            else
+            {
+                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, static_cast<std::string>(keyInStr).length()).c_str());
+            }
+            startOffset = endOffset + 1;
+        }
+    }
+
+    const char* keyOutStr = animationProperties->getString("keyOut");
+    float* keyOut = NULL;
+    if (keyOutStr)
+    {
+        keyOut = new float[components];
+        startOffset = 0;
+        endOffset = std::string::npos;
+        for (unsigned int i = 0; i < components; i++)
+        {
+            endOffset = static_cast<std::string>(keyOutStr).find_first_of(delimeter, startOffset);
+            if (endOffset != std::string::npos)
+            {
+                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, endOffset - startOffset).c_str());
+            }
+            else
+            {
+                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, static_cast<std::string>(keyOutStr).length()).c_str());
+            }
+            startOffset = endOffset + 1;
+        }
+    }
+
+    int curve = Curve::getInterpolationType(curveStr);
+
+    Animation* animation = NULL;
+    if (keyIn && keyOut)
+    {
+        animation = createAnimation(id, propertyId, keyCount, keyTimes, keyValues, keyIn, keyOut, (Curve::InterpolationType)curve);
+    }
+    else
+    {
+        animation = createAnimation(id, propertyId, keyCount, keyTimes, keyValues, (Curve::InterpolationType) curve);
+    }
+
+    const char* repeat = animationProperties->getString("repeatCount");
+    if (repeat)
+    {
+        if (strcmp(repeat, ANIMATION_TARGET_INDEFINITE_STR) == 0)
+        {
+            animation->getClip()->setRepeatCount(AnimationClip::REPEAT_INDEFINITE);
+        }
+        else
+        {
+            float value;
+            sscanf(repeat, "%f", &value);
+            animation->getClip()->setRepeatCount(value);
+        }
+    }
+    
+    SAFE_DELETE_ARRAY(keyOut);
+    SAFE_DELETE_ARRAY(keyIn);
+    SAFE_DELETE_ARRAY(keyValues);
+    SAFE_DELETE_ARRAY(keyTimes);
+
+    Properties* pClip = animationProperties->getNextNamespace();
+    if (pClip && std::strcmp(pClip->getNamespace(), "clip") == 0)
+    {
+        int frameCount = animationProperties->getInt("frameCount");
+        if (frameCount <= 0)
+        {
+            GP_ERROR("Frame count must be greater than zero for a clip.");
+            return animation;
+        }
+        animation->createClips(animationProperties, (unsigned int) frameCount);
+    }
+    
+    return animation;
+}
+
+void AnimationTarget::destroyAnimation(const char* id)
+{
+    // Find the animation with the specified ID.
+    Animation::Channel* channel = getChannel(id);
+    if (channel == NULL)
+        return;
+
+    // Remove this target's channel from animation, and from the target's list of channels.
+    GP_ASSERT(channel->_animation);
+    channel->_animation->removeChannel(channel);
+    removeChannel(channel);
+
+    SAFE_DELETE(channel);
+}
+
+Animation* AnimationTarget::getAnimation(const char* id) const
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+        GP_ASSERT(*itr);
+
+        if (id == NULL)
+            return (*itr)->_animation;
+
+        Animation::Channel* channel = NULL;
+        for (; itr != _animationChannels->end(); itr++)
+        {
+            channel = (Animation::Channel*)(*itr);
+            GP_ASSERT(channel);
+            GP_ASSERT(channel->_animation);
+            if (channel->_animation->_id.compare(id) == 0)
+            {
+                return channel->_animation;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
+{
+    GP_ASSERT(propertyIdStr);
+
+    if (type == AnimationTarget::TRANSFORM)
+    {
+        if (strcmp(propertyIdStr, "ANIMATE_SCALE") == 0)
+        {
+            return Transform::ANIMATE_SCALE;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_X") == 0)
+        {
+            return Transform::ANIMATE_SCALE_X;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_Y") == 0)
+        {
+            return Transform::ANIMATE_SCALE_Y;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_Z") == 0)
+        {
+            return Transform::ANIMATE_SCALE_Z;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_ROTATE") == 0)
+        {
+            return Transform::ANIMATE_ROTATE;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE") == 0)
+        {
+            return Transform::ANIMATE_TRANSLATE;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE_X") == 0)
+        {
+            return Transform::ANIMATE_TRANSLATE_X;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE_Y") == 0)
+        {
+            return Transform::ANIMATE_TRANSLATE_Y;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_TRANSLATE_Z") == 0)
+        {
+            return Transform::ANIMATE_TRANSLATE_Z;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_ROTATE_TRANSLATE") == 0)
+        {
+            return Transform::ANIMATE_ROTATE_TRANSLATE;
+        }
+        else if (strcmp(propertyIdStr, "ANIMATE_SCALE_ROTATE_TRANSLATE") == 0)
+        {
+            return Transform::ANIMATE_SCALE_ROTATE_TRANSLATE;
+        }
+    }
+    else
+    {
+        if (strcmp(propertyIdStr, "ANIMATE_UNIFORM") == 0)
+        {
+            return MaterialParameter::ANIMATE_UNIFORM;
+        }
+    }
+
+    return -1;
+}
+
+void AnimationTarget::addChannel(Animation::Channel* channel)
+{
+    if (_animationChannels == NULL)
+        _animationChannels = new std::vector<Animation::Channel*>;
+
+    GP_ASSERT(channel);
+    _animationChannels->push_back(channel);
+}
+
+void AnimationTarget::removeChannel(Animation::Channel* channel)
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+        for ( ; itr != _animationChannels->end(); itr++)
+        {
+            Animation::Channel* temp = *itr;
+            if (channel == temp)
+            {
+                _animationChannels->erase(itr);
+
+                if (_animationChannels->empty())
+                    SAFE_DELETE(_animationChannels);
+
+                return;
+            }
+        }
+    }
+}
+
+Animation::Channel* AnimationTarget::getChannel(const char* id) const
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+
+        if (id == NULL)
+            return (*itr);
+
+        Animation::Channel* channel = NULL;
+        for (; itr != _animationChannels->end(); itr++)
+        {
+            channel = (Animation::Channel*)(*itr);
+            GP_ASSERT(channel);
+            if (channel->_animation->_id.compare(id) == 0)
+            {
+                return channel;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+void AnimationTarget::cloneInto(AnimationTarget* target, NodeCloneContext &context) const
+{
+    if (_animationChannels)
+    {
+        for (std::vector<Animation::Channel*>::const_iterator it = _animationChannels->begin(); it != _animationChannels->end(); ++it)
+        {
+            Animation::Channel* channel = *it;
+            GP_ASSERT(channel);
+            GP_ASSERT(channel->_animation);
+
+            Animation* animation = context.findClonedAnimation(channel->_animation);
+            if (animation != NULL)
+            {
+                Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
+                animation->addChannel(channelCopy);
+            }
+            else
+            {
+                // Clone the animation and register it with the context so that it only gets cloned once.
+                animation = channel->_animation->clone(channel, target);
+                context.registerClonedAnimation(channel->_animation, animation);
+            }
+        }
+    }
+}
+
+void AnimationTarget::convertByValues(unsigned int propertyId, unsigned int componentCount, float* from, float* by)
+{
+    if (_targetType == AnimationTarget::TRANSFORM)
+    {
+        switch(propertyId)
+        {
+            case Transform::ANIMATE_SCALE:
+            case Transform::ANIMATE_SCALE_UNIT:
+            case Transform::ANIMATE_SCALE_X:
+            case Transform::ANIMATE_SCALE_Y:
+            case Transform::ANIMATE_SCALE_Z:
+            {
+                convertScaleByValues(from, by, componentCount);
+                break;
+            }
+            case Transform::ANIMATE_TRANSLATE:
+            case Transform::ANIMATE_TRANSLATE_X:
+            case Transform::ANIMATE_TRANSLATE_Y:
+            case Transform::ANIMATE_TRANSLATE_Z:
+            {
+                convertByValues(from, by, componentCount);
+                break;
+            }
+            case Transform::ANIMATE_ROTATE:
+            {
+                convertQuaternionByValues(from, by);
+                break;
+            }
+            case Transform::ANIMATE_ROTATE_TRANSLATE:
+            {
+                convertQuaternionByValues(from, by);
+                convertByValues(from + 4, by + 4, 3);
+                break;
+            }
+            case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
+            {
+                convertScaleByValues(from, by, 3);
+                convertQuaternionByValues(from + 3, by + 3);
+                convertByValues(from + 7, by + 7, 3);
+                break;
+            }
+        }
+    }
+    else
+    {
+        convertByValues(from, by, componentCount);
+    }
+}
+
+void AnimationTarget::convertQuaternionByValues(float* from, float* by)
+{
+    Quaternion qFrom(from);
+    Quaternion qBy(by);
+    qBy.multiply(qFrom);
+    memcpy(by, (float*)&qBy, sizeof(float) * 4);
+}
+
+void AnimationTarget::convertScaleByValues(float* from, float* by, unsigned int componentCount)
+{
+    for (unsigned int i = 0; i < componentCount; i++)
+    {
+        by[i] *= from[i];
+    }
+}
+
+void AnimationTarget::convertByValues(float* from, float* by, unsigned int componentCount)
+{
+    for (unsigned int i = 0; i < componentCount; i++)
+    {
+        by[i] += from[i];
+    }
+}
+
+}
+
+
+

+ 248 - 248
gameplay/src/AnimationTarget.h

@@ -1,248 +1,248 @@
-#ifndef ANIMATIONTARGET_H_
-#define ANIMATIONTARGET_H_
-
-#include "Curve.h"
-#include "AnimationController.h"
-
-namespace gameplay
-{
-
-class Animation;
-class AnimationValue;
-class NodeCloneContext;
-
-/**
- * Defines an interface allowing animation to target
- * an object for changing its animation properties.
- */
-class AnimationTarget
-{
-    friend class Animation;
-    friend class AnimationClip;
-
-public:
-
-    /**
-     * Creates an animation on this target from a set of key value and key time pairs.
-     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
-     *
-     * @param id The ID of the animation.
-     * @param propertyId The property on this target to animate.
-     * @param keyCount The number of keyframes in the animation. Must be greater than one.
-     * @param keyTimes The list of key times for the animation (in milliseconds).
-     * @param keyValues The list of key values for the animation.
-     * @param type The curve interpolation type.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, Curve::InterpolationType type);
-
-    /**
-     * Creates an animation on this target from a set of key value and key time pairs.
-     *
-     * @param id The ID of the animation.
-     * @param propertyId The property on this target to animate.
-     * @param keyCount The number of keyframes in the animation. Must be greater than one.
-     * @param keyTimes The list of key times for the animation (in milliseconds).
-     * @param keyValues The list of key values for the animation.
-     * @param keyInValue The list of key in values for the animation.
-     * @param keyOutValue The list of key out values for the animation.
-     * @param type The curve interpolation type.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type);
-
-    /**
-     * Creates an animation on this target using the data from the Properties object defined at the specified URL,
-     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
-     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
-     *
-     * @param id The ID of the animation.
-     * @param url The URL pointing to the Properties object defining the animation data.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, const char* url);
-
-    /**
-     * Creates an animation on this target using the data from the given properties object.
-     *
-     * @param id The ID of the animation.
-     * @param animationProperties The properties object defining the animation data.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, Properties* animationProperties);
-
-    /**
-     * Creates a simple two keyframe from-to animation.
-     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
-     *
-     * @param id The ID of the animation.
-     * @param propertyId The property on this target to animate.
-     * @param from The values to animate from.
-     * @param to The values to animate to.
-     * @param type The curve interpolation type.
-     * @param duration The duration of the animation (in milliseconds).
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration);
-
-    /**
-     * Creates a simple two keyframe from-by animation.
-     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
-     *
-     * @param id The ID of the animation.
-     * @param propertyId The property on this target to animate.
-     * @param from The values to animate from.
-     * @param by The values to animate by.
-     * @param type The curve interpolation type.
-     * @param duration The duration of the animation (in milliseconds).
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration);
-
-    /**
-     * Destroys the animation with the specified ID. Destroys the first animation if ID is NULL.
-     *
-     * @param id The ID of the animation to destroy.
-     */
-    void destroyAnimation(const char* id = NULL);
-
-    /**
-     * Abstract method to return the property component count of the given property ID on the AnimationTarget.
-     *
-     * @param propertyId The ID of the property on the AnimationTarget to obtain the component count for.
-     *
-     * @return The property component count of the given property.
-     */
-    virtual unsigned int getAnimationPropertyComponentCount(int propertyId) const = 0;
-
-    /**
-     * Abstract method for getting the animation property value for the given property ID on the AnimationTarget.
-     *
-     * @param propertyId The ID of the property on the AnimationTarget to get the animation property value for.
-     * @param value The container to get the animation property value in.
-     */
-    virtual void getAnimationPropertyValue(int propertyId, AnimationValue* value) = 0;
-
-    /**
-     * Abstract method for setting the animation property value for the given property ID on the AnimationTarget.
-     *
-     * @param propertyId The ID of the property on the AnimationTarget to set the animation property value on.
-     * @param value The container to set the animation property value in.
-     * @param blendWeight The blend weight.
-     */
-    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f) = 0;
-
-    /**
-     * Gets the animation with the specified ID. If the ID is NULL, this function will return the first animation it finds.
-     *
-     * @param id The name of the animation to get.
-     */
-    Animation* getAnimation(const char* id = NULL) const;
-
-protected:
-
-    /**
-     * The type of animation target.
-     */
-    enum TargetType
-    {
-        SCALAR,
-        TRANSFORM
-    };
-
-    /**
-     * Constructor.
-     */
-    AnimationTarget();
-
-    /**
-     * Destructor.
-     */
-    virtual ~AnimationTarget();
-
-    /**
-     * Adds the given animation channel to this animation target.
-     *
-     * @param channel The animation channel to add.
-     */
-    void addChannel(Animation::Channel* channel);
-
-    /**
-     * Removes the given animation channel from this animation target.
-     *
-     * @param channel The animation channel to delete.
-     */
-    void removeChannel(Animation::Channel* channel);
-
-    /**
-     * Gets the Animation::Channel that belongs to the Animation with the specified ID.
-     *
-     * @param id The ID of the Animation the Channel belongs to.
-     */
-    Animation::Channel* getChannel(const char* id) const;
-
-    /**
-     * Copies data from this animation target into the given target for the purpose of cloning.
-     *
-     * @param target The target to copy into.
-     * @param context The clone context.
-     */
-    void cloneInto(AnimationTarget* target, NodeCloneContext &context) const;
-
-    /**
-     * The target's type.
-     *
-     * @see TargetType::SCALAR
-     * @see TargetType::TRANSFORM
-     */
-    TargetType _targetType;
-
-private:
-
-    /**
-     * Constructor.
-     */
-    AnimationTarget(const AnimationTarget& copy);
-
-    /**
-     * Gets the TargetType's property ID value for the specified property ID string.
-     *
-     * @param type The TargetType of the AnimationTarget.
-     * @param propertyIdStr The property ID string.
-     * @return The property ID value for the property ID string; -1 if the propertyIdStr does not exist
-     *    for the TargetType.
-     */
-    static int getPropertyId(TargetType type, const char* propertyIdStr);
-
-    /**
-     * Converts by-value animations to to-value animations.
-     */
-    void convertByValues(unsigned int propertyId, unsigned int componentCount, float* from, float* by);
-
-    /**
-     * Converts a Quaternion by-value into a to-value.
-     */
-    void convertQuaternionByValues(float* from, float* by);
-
-    /**
-     * Converts a Scale by-value into a to-value.
-     */
-    void convertScaleByValues(float* from, float* by, unsigned int componentCount);
-
-    /**
-     * Converts a by-value into a to-value.
-     */
-    void convertByValues(float* from, float* by, unsigned int componentCount);
-
-    std::vector<Animation::Channel*>* _animationChannels;   // Collection of all animation channels that target the AnimationTarget
-
-};
-}
-
-#endif
+#ifndef ANIMATIONTARGET_H_
+#define ANIMATIONTARGET_H_
+
+#include "Curve.h"
+#include "AnimationController.h"
+
+namespace gameplay
+{
+
+class Animation;
+class AnimationValue;
+class NodeCloneContext;
+
+/**
+ * Defines an interface allowing animation to target
+ * an object for changing its animation properties.
+ */
+class AnimationTarget
+{
+    friend class Animation;
+    friend class AnimationClip;
+
+public:
+
+    /**
+     * Creates an animation on this target from a set of key value and key time pairs.
+     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
+     *
+     * @param id The ID of the animation.
+     * @param propertyId The property on this target to animate.
+     * @param keyCount The number of keyframes in the animation. Must be greater than one.
+     * @param keyTimes The list of key times for the animation (in milliseconds).
+     * @param keyValues The list of key values for the animation.
+     * @param type The curve interpolation type.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, Curve::InterpolationType type);
+
+    /**
+     * Creates an animation on this target from a set of key value and key time pairs.
+     *
+     * @param id The ID of the animation.
+     * @param propertyId The property on this target to animate.
+     * @param keyCount The number of keyframes in the animation. Must be greater than one.
+     * @param keyTimes The list of key times for the animation (in milliseconds).
+     * @param keyValues The list of key values for the animation.
+     * @param keyInValue The list of key in values for the animation.
+     * @param keyOutValue The list of key out values for the animation.
+     * @param type The curve interpolation type.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned int* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type);
+
+    /**
+     * Creates an animation on this target using the data from the Properties object defined at the specified URL,
+     * where the URL is of the format "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>"
+     * (and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
+     *
+     * @param id The ID of the animation.
+     * @param url The URL pointing to the Properties object defining the animation data.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, const char* url);
+
+    /**
+     * Creates an animation on this target using the data from the given properties object.
+     *
+     * @param id The ID of the animation.
+     * @param animationProperties The properties object defining the animation data.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, Properties* animationProperties);
+
+    /**
+     * Creates a simple two keyframe from-to animation.
+     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
+     *
+     * @param id The ID of the animation.
+     * @param propertyId The property on this target to animate.
+     * @param from The values to animate from.
+     * @param to The values to animate to.
+     * @param type The curve interpolation type.
+     * @param duration The duration of the animation (in milliseconds).
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration);
+
+    /**
+     * Creates a simple two keyframe from-by animation.
+     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
+     *
+     * @param id The ID of the animation.
+     * @param propertyId The property on this target to animate.
+     * @param from The values to animate from.
+     * @param by The values to animate by.
+     * @param type The curve interpolation type.
+     * @param duration The duration of the animation (in milliseconds).
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration);
+
+    /**
+     * Destroys the animation with the specified ID. Destroys the first animation if ID is NULL.
+     *
+     * @param id The ID of the animation to destroy.
+     */
+    void destroyAnimation(const char* id = NULL);
+
+    /**
+     * Abstract method to return the property component count of the given property ID on the AnimationTarget.
+     *
+     * @param propertyId The ID of the property on the AnimationTarget to obtain the component count for.
+     *
+     * @return The property component count of the given property.
+     */
+    virtual unsigned int getAnimationPropertyComponentCount(int propertyId) const = 0;
+
+    /**
+     * Abstract method for getting the animation property value for the given property ID on the AnimationTarget.
+     *
+     * @param propertyId The ID of the property on the AnimationTarget to get the animation property value for.
+     * @param value The container to get the animation property value in.
+     */
+    virtual void getAnimationPropertyValue(int propertyId, AnimationValue* value) = 0;
+
+    /**
+     * Abstract method for setting the animation property value for the given property ID on the AnimationTarget.
+     *
+     * @param propertyId The ID of the property on the AnimationTarget to set the animation property value on.
+     * @param value The container to set the animation property value in.
+     * @param blendWeight The blend weight.
+     */
+    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f) = 0;
+
+    /**
+     * Gets the animation with the specified ID. If the ID is NULL, this function will return the first animation it finds.
+     *
+     * @param id The name of the animation to get.
+     */
+    Animation* getAnimation(const char* id = NULL) const;
+
+protected:
+
+    /**
+     * The type of animation target.
+     */
+    enum TargetType
+    {
+        SCALAR,
+        TRANSFORM
+    };
+
+    /**
+     * Constructor.
+     */
+    AnimationTarget();
+
+    /**
+     * Destructor.
+     */
+    virtual ~AnimationTarget();
+
+	/**
+     * Gets the TargetType's property ID value for the specified property ID string.
+     *
+     * @param type The TargetType of the AnimationTarget.
+     * @param propertyIdStr The property ID string.
+     * @return The property ID value for the property ID string; -1 if the propertyIdStr does not exist
+     *    for the TargetType.
+     */
+    virtual int getPropertyId(TargetType type, const char* propertyIdStr);
+
+    /**
+     * Adds the given animation channel to this animation target.
+     *
+     * @param channel The animation channel to add.
+     */
+    void addChannel(Animation::Channel* channel);
+
+    /**
+     * Removes the given animation channel from this animation target.
+     *
+     * @param channel The animation channel to delete.
+     */
+    void removeChannel(Animation::Channel* channel);
+
+    /**
+     * Gets the Animation::Channel that belongs to the Animation with the specified ID.
+     *
+     * @param id The ID of the Animation the Channel belongs to.
+     */
+    Animation::Channel* getChannel(const char* id) const;
+
+    /**
+     * Copies data from this animation target into the given target for the purpose of cloning.
+     *
+     * @param target The target to copy into.
+     * @param context The clone context.
+     */
+    void cloneInto(AnimationTarget* target, NodeCloneContext &context) const;
+
+    /**
+     * The target's type.
+     *
+     * @see TargetType::SCALAR
+     * @see TargetType::TRANSFORM
+     */
+    TargetType _targetType;
+
+private:
+
+    /**
+     * Constructor.
+     */
+    AnimationTarget(const AnimationTarget& copy);
+
+    /**
+     * Converts by-value animations to to-value animations.
+     */
+    void convertByValues(unsigned int propertyId, unsigned int componentCount, float* from, float* by);
+
+    /**
+     * Converts a Quaternion by-value into a to-value.
+     */
+    void convertQuaternionByValues(float* from, float* by);
+
+    /**
+     * Converts a Scale by-value into a to-value.
+     */
+    void convertScaleByValues(float* from, float* by, unsigned int componentCount);
+
+    /**
+     * Converts a by-value into a to-value.
+     */
+    void convertByValues(float* from, float* by, unsigned int componentCount);
+
+    std::vector<Animation::Channel*>* _animationChannels;   // Collection of all animation channels that target the AnimationTarget
+
+};
+}
+
+#endif

+ 73 - 73
gameplay/src/AnimationValue.cpp

@@ -1,73 +1,73 @@
-#include "Base.h"
-#include "AnimationValue.h"
-
-namespace gameplay
-{
-
-AnimationValue::AnimationValue(unsigned int componentCount)
-  : _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
-{
-    GP_ASSERT(_componentCount > 0);
-    _value = new float[_componentCount];
-}
-
-AnimationValue::AnimationValue(const AnimationValue& copy)
-{
-    _value = new float[copy._componentCount];
-    _componentSize = copy._componentSize;
-    _componentCount = copy._componentCount;
-    memcpy(_value, copy._value, _componentSize);
-}
-
-AnimationValue::~AnimationValue()
-{
-    SAFE_DELETE_ARRAY(_value);
-}
-
-AnimationValue& AnimationValue::operator=(const AnimationValue& v)
-{
-    if (this != &v)
-    {
-        if (_value == NULL || _componentSize != v._componentSize || _componentCount != v._componentCount)
-        {
-            _componentSize = v._componentSize;
-            _componentCount = v._componentCount;
-            SAFE_DELETE_ARRAY(_value);
-            _value = new float[v._componentCount];
-        }
-        memcpy(_value, v._value, _componentSize);
-    }
-    return *this;
-}
-
-float AnimationValue::getFloat(unsigned int index) const
-{
-    GP_ASSERT(index < _componentCount);
-    GP_ASSERT(_value);
-
-    return _value[index];
-}
-
-void AnimationValue::setFloat(unsigned int index, float value)
-{
-    GP_ASSERT(index < _componentCount);
-    GP_ASSERT(_value);
-
-    _value[index] = value;
-}
-
-void AnimationValue::getFloats(unsigned int index, float* values, unsigned int count) const
-{
-    GP_ASSERT(_value && values && index < _componentCount && (index + count) <= _componentCount);
-
-    memcpy(values, &_value[index], count * sizeof(float));
-}
-
-void AnimationValue::setFloats(unsigned int index, float* values, unsigned int count)
-{
-    GP_ASSERT(_value && values && index < _componentCount && (index + count) <= _componentCount);
-
-    memcpy(&_value[index], values, count * sizeof(float));
-}
-
-}
+#include "Base.h"
+#include "AnimationValue.h"
+
+namespace gameplay
+{
+
+AnimationValue::AnimationValue(unsigned int componentCount)
+  : _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
+{
+    GP_ASSERT(_componentCount > 0);
+    _value = new float[_componentCount];
+}
+
+AnimationValue::AnimationValue(const AnimationValue& copy)
+{
+    _value = new float[copy._componentCount];
+    _componentSize = copy._componentSize;
+    _componentCount = copy._componentCount;
+    memcpy(_value, copy._value, _componentSize);
+}
+
+AnimationValue::~AnimationValue()
+{
+    SAFE_DELETE_ARRAY(_value);
+}
+
+AnimationValue& AnimationValue::operator=(const AnimationValue& v)
+{
+    if (this != &v)
+    {
+        if (_value == NULL || _componentSize != v._componentSize || _componentCount != v._componentCount)
+        {
+            _componentSize = v._componentSize;
+            _componentCount = v._componentCount;
+            SAFE_DELETE_ARRAY(_value);
+            _value = new float[v._componentCount];
+        }
+        memcpy(_value, v._value, _componentSize);
+    }
+    return *this;
+}
+
+float AnimationValue::getFloat(unsigned int index) const
+{
+    GP_ASSERT(index < _componentCount);
+    GP_ASSERT(_value);
+
+    return _value[index];
+}
+
+void AnimationValue::setFloat(unsigned int index, float value)
+{
+    GP_ASSERT(index < _componentCount);
+    GP_ASSERT(_value);
+
+    _value[index] = value;
+}
+
+void AnimationValue::getFloats(unsigned int index, float* values, unsigned int count) const
+{
+    GP_ASSERT(_value && values && index < _componentCount && (index + count) <= _componentCount);
+
+    memcpy(values, &_value[index], count * sizeof(float));
+}
+
+void AnimationValue::setFloats(unsigned int index, float* values, unsigned int count)
+{
+    GP_ASSERT(_value && values && index < _componentCount && (index + count) <= _componentCount);
+
+    memcpy(&_value[index], values, count * sizeof(float));
+}
+
+}

+ 88 - 88
gameplay/src/AnimationValue.h

@@ -1,88 +1,88 @@
-#ifndef ANIMATIONVALUE_H_
-#define ANIMATIONVALUE_H_
-
-#include "Animation.h"
-
-namespace gameplay
-{
-
-/**
- * The runtime interface to represent an animation value.
- */
-class AnimationValue
-{
-    friend class AnimationClip;
-
-public:
-
-    /**
-     * Gets the value at the specified index.
-     *
-     * @param index The index of the component to get the value for.
-     *
-     * @return The float value at the specified index.
-     */
-    float getFloat(unsigned int index) const;
-
-    /**
-     * Sets the value at the specified index.
-     *
-     * @param index The index of the component to set the value for.
-     * @param value The value to set the component to.
-     */
-    void setFloat(unsigned int index, float value);
-
-    /**
-     * Copies one or more float values from this AnimationValue into the specified array.
-     *
-     * @param index The index to start copying from.
-     * @param values Pointer to float array to copy values into.
-     * @param count Number of values to copy.
-     */
-    void getFloats(unsigned int index, float* values, unsigned int count) const;
-
-    /**
-     * Copies one or more float values into the AnimationValue.
-     *
-     * @param index The index of the first component to set the value for.
-     * @param values Array of values to copy into the AnimationValue.
-     * @param count Number of values to in the array to copy in.
-     */
-    void setFloats(unsigned int index, float* values, unsigned int count);
-
-private:
-
-    /**
-     * Constructor.
-     */
-    AnimationValue();
-
-    /**
-     * Constructor.
-     */
-    AnimationValue(unsigned int componentCount);
-
-    /**
-     * Constructor.
-     */
-    AnimationValue(const AnimationValue& copy);
-
-    /**
-     * Destructor.
-     */
-    ~AnimationValue();
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    AnimationValue& operator=(const AnimationValue& v);
-
-    unsigned int _componentCount;   // The number of float values for the property.
-    unsigned int _componentSize;    // The number of bytes of memory the property is.
-    float* _value;                  // The current value of the property.
-
-};
-
-}
-
-#endif
+#ifndef ANIMATIONVALUE_H_
+#define ANIMATIONVALUE_H_
+
+#include "Animation.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a running animation value which can have one or more floats.
+ */
+class AnimationValue
+{
+    friend class AnimationClip;
+
+public:
+
+    /**
+     * Gets the value at the specified index.
+     *
+     * @param index The index of the component to get the value for.
+     *
+     * @return The float value at the specified index.
+     */
+    float getFloat(unsigned int index) const;
+
+    /**
+     * Sets the value at the specified index.
+     *
+     * @param index The index of the component to set the value for.
+     * @param value The value to set the component to.
+     */
+    void setFloat(unsigned int index, float value);
+
+    /**
+     * Copies one or more float values from this AnimationValue into the specified array.
+     *
+     * @param index The index to start copying from.
+     * @param values Pointer to float array to copy values into.
+     * @param count Number of values to copy.
+     */
+    void getFloats(unsigned int index, float* values, unsigned int count) const;
+
+    /**
+     * Copies one or more float values into the AnimationValue.
+     *
+     * @param index The index of the first component to set the value for.
+     * @param values Array of values to copy into the AnimationValue.
+     * @param count Number of values to in the array to copy in.
+     */
+    void setFloats(unsigned int index, float* values, unsigned int count);
+
+private:
+
+    /**
+     * Constructor.
+     */
+    AnimationValue();
+
+    /**
+     * Constructor.
+     */
+    AnimationValue(unsigned int componentCount);
+
+    /**
+     * Constructor.
+     */
+    AnimationValue(const AnimationValue& copy);
+
+    /**
+     * Destructor.
+     */
+    ~AnimationValue();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    AnimationValue& operator=(const AnimationValue& v);
+
+    unsigned int _componentCount;   // The number of float values for the property.
+    unsigned int _componentSize;    // The number of bytes of memory the property is.
+    float* _value;                  // The current value of the property.
+
+};
+
+}
+
+#endif

+ 403 - 403
gameplay/src/AudioBuffer.cpp

@@ -1,403 +1,403 @@
-#include "Base.h"
-#include "AudioBuffer.h"
-#include "FileSystem.h"
-
-namespace gameplay
-{
-
-// Audio buffer cache
-static std::vector<AudioBuffer*> __buffers;
-
-// Callbacks for loading an ogg file using Stream
-static size_t readStream(void *ptr, size_t size, size_t nmemb, void *datasource)
-{
-    GP_ASSERT(datasource);
-    Stream* stream = reinterpret_cast<Stream*>(datasource);
-    return stream->read(ptr, size, nmemb);
-}
-
-static int seekStream(void *datasource, ogg_int64_t offset, int whence)
-{
-    GP_ASSERT(datasource);
-    Stream* stream = reinterpret_cast<Stream*>(datasource);
-    return !stream->seek(offset, whence);
-}
-
-static int closeStream(void *datasource)
-{
-    GP_ASSERT(datasource);
-    Stream* stream = reinterpret_cast<Stream*>(datasource);
-    stream->close();
-    return 0;
-}
-
-static long tellStream(void *datasource)
-{
-    GP_ASSERT(datasource);
-    Stream* stream = reinterpret_cast<Stream*>(datasource);
-    return stream->position();
-}
-
-AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
-    : _filePath(path), _alBuffer(buffer)
-{
-}
-
-AudioBuffer::~AudioBuffer()
-{
-    // Remove the buffer from the cache.
-    unsigned int bufferCount = (unsigned int)__buffers.size();
-    for (unsigned int i = 0; i < bufferCount; i++)
-    {
-        if (this == __buffers[i])
-        {
-            __buffers.erase(__buffers.begin() + i);
-            break;
-        }
-    }
-
-    if (_alBuffer)
-    {
-        AL_CHECK( alDeleteBuffers(1, &_alBuffer) );
-        _alBuffer = 0;
-    }
-}
-
-AudioBuffer* AudioBuffer::create(const char* path)
-{
-    GP_ASSERT(path);
-
-    // Search the cache for a stream from this file.
-    unsigned int bufferCount = (unsigned int)__buffers.size();
-    AudioBuffer* buffer = NULL;
-    for (unsigned int i = 0; i < bufferCount; i++)
-    {
-        buffer = __buffers[i];
-        GP_ASSERT(buffer);
-        if (buffer->_filePath.compare(path) == 0)
-        {
-            buffer->addRef();
-            return buffer;
-        }
-    }
-
-    ALuint alBuffer;
-
-    // Load audio data into a buffer.
-    AL_CHECK( alGenBuffers(1, &alBuffer) );
-    if (AL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to create OpenAL buffer; alGenBuffers error: %d", AL_LAST_ERROR());
-        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
-        return NULL;
-    }
-    
-    // Load sound file.
-    std::auto_ptr<Stream> stream(FileSystem::open(path));
-    if (stream.get() == NULL || !stream->canRead())
-    {
-        GP_ERROR("Failed to load audio file %s.", path);
-        goto cleanup;
-    }
-    
-    // Read the file header
-    char header[12];
-    if (stream->read(header, 1, 12) != 12)
-    {
-        GP_ERROR("Invalid header for audio file %s.", path);
-        goto cleanup;
-    }
-    
-    // Check the file format
-    if (memcmp(header, "RIFF", 4) == 0)
-    {
-        if (!AudioBuffer::loadWav(stream.get(), alBuffer))
-        {
-            GP_ERROR("Invalid wave file: %s", path);
-            goto cleanup;
-        }
-    }
-    else if (memcmp(header, "OggS", 4) == 0)
-    {
-        if (!AudioBuffer::loadOgg(stream.get(), alBuffer))
-        {
-            GP_ERROR("Invalid ogg file: %s", path);
-            goto cleanup;
-        }
-    }
-    else
-    {
-        GP_ERROR("Unsupported audio file: %s", path);
-        goto cleanup;
-    }
-
-    buffer = new AudioBuffer(path, alBuffer);
-
-    // Add the buffer to the cache.
-    __buffers.push_back(buffer);
-
-    return buffer;
-    
-cleanup:
-    if (alBuffer)
-        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
-    return NULL;
-}
-
-bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
-{
-    GP_ASSERT(stream);
-
-    unsigned char data[12];
-    
-    // Verify the wave fmt magic value meaning format.
-    if (stream->read(data, 1, 8) != 8 || memcmp(data, "fmt ", 4) != 0 )
-    {
-        GP_ERROR("Failed to verify the magic value for the wave file format.");
-        return false;
-    }
-    
-    unsigned int section_size;
-    section_size  = data[7]<<24;
-    section_size |= data[6]<<16;
-    section_size |= data[5]<<8;
-    section_size |= data[4];
-
-    // Check for a valid pcm format.
-    if (stream->read(data, 1, 2) != 2 || data[1] != 0 || data[0] != 1)
-    {
-        GP_ERROR("Unsupported audio file format (must be a valid PCM format).");
-        return false;
-    }
-    
-    // Get the channel count (16-bit little-endian).
-    int channels;
-    if (stream->read(data, 1, 2) != 2)
-    {
-        GP_ERROR("Failed to read the wave file's channel count.");
-        return false;
-    }
-    channels  = data[1]<<8;
-    channels |= data[0];
-    
-    // Get the sample frequency (32-bit little-endian).
-    ALuint frequency;
-    if (stream->read(data, 1, 4) != 4)
-    {
-        GP_ERROR("Failed to read the wave file's sample frequency.");
-        return false;
-    }
-
-    frequency  = data[3]<<24;
-    frequency |= data[2]<<16;
-    frequency |= data[1]<<8;
-    frequency |= data[0];
-    
-    // The next 6 bytes hold the block size and bytes-per-second. 
-    // We don't need that info, so just read and ignore it. 
-    // We could use this later if we need to know the duration.
-    if (stream->read(data, 1, 6) != 6)
-    {
-        GP_ERROR("Failed to read past the wave file's block size and bytes-per-second.");
-        return false;
-    }
-    
-    // Get the bit depth (16-bit little-endian).
-    int bits;
-    if (stream->read(data, 1, 2) != 2)
-    {
-        GP_ERROR("Failed to read the wave file's bit depth.");
-        return false;
-    }
-    bits  = data[1]<<8;
-    bits |= data[0];
-    
-    // Now convert the given channel count and bit depth into an OpenAL format. 
-    ALuint format = 0;
-    if (bits == 8)
-    {
-        if (channels == 1)
-            format = AL_FORMAT_MONO8;
-        else if (channels == 2)
-            format = AL_FORMAT_STEREO8;
-    }
-    else if (bits == 16)
-    {
-        if (channels == 1)
-            format = AL_FORMAT_MONO16;
-        else if (channels == 2)
-            format = AL_FORMAT_STEREO16;
-    }
-    else
-    {
-        GP_ERROR("Incompatible wave file format: (%d, %d)", channels, bits);
-        return false;
-    }
-    
-    // Check against the size of the format header as there may be more data that we need to read.
-    if (section_size > 16)
-    {
-        unsigned int length = section_size - 16;
-
-        // Extension size is 2 bytes.
-        if (stream->read(data, 1, length) != length)
-        {
-            GP_ERROR("Failed to read extension size from wave file.");
-            return false;
-        }
-    }
-
-    // Read in the rest of the file a chunk (section) at a time.
-    while (true)
-    {
-        // Check if we are at the end of the file without reading the data.
-        if (stream->eof())
-        {
-            GP_ERROR("Failed to load wave file; file appears to have no data.");
-            return false;
-        }
-
-        // Read in the type of the next section of the file.
-        if (stream->read(data, 1, 4) != 4)
-        {
-            GP_ERROR("Failed to read next section type from wave file.");
-            return false;
-        }
-
-        // Data chunk.
-        if (memcmp(data, "data", 4) == 0)
-        {
-            // Read how much data is remaining and buffer it up.
-            unsigned int dataSize;
-            if (stream->read(&dataSize, sizeof(int), 1) != 1)
-            {
-                GP_ERROR("Failed to read size of data section from wave file.");
-                return false;
-            }
-
-            char* data = new char[dataSize];
-            if (stream->read(data, sizeof(char), dataSize) != dataSize)
-            {
-                GP_ERROR("Failed to load wave file; file is missing data.");
-                SAFE_DELETE_ARRAY(data);
-                return false;
-            }
-
-            AL_CHECK( alBufferData(buffer, format, data, dataSize, frequency) );
-            SAFE_DELETE_ARRAY(data);
-
-            // We've read the data, so return now.
-            return true;
-        }
-        // Other chunk - could be any of the following:
-        // - Fact ("fact")
-        // - Wave List ("wavl")
-        // - Silent ("slnt")
-        // - Cue ("cue ")
-        // - Playlist ("plst")
-        // - Associated Data List ("list")
-        // - Label ("labl")
-        // - Note ("note")
-        // - Labeled Text ("ltxt")
-        // - Sampler ("smpl")
-        // - Instrument ("inst")
-        else
-        {
-            // Store the name of the chunk so we can report errors informatively.
-            char chunk[5] = { 0 };
-            memcpy(chunk, data, 4);
-
-            // Read the chunk size.
-            if (stream->read(data, 1, 4) != 4)
-            {
-                GP_ERROR("Failed to read size of '%s' chunk from wave file.", chunk);
-                return false;
-            }
-
-            section_size  = data[3]<<24;
-            section_size |= data[2]<<16;
-            section_size |= data[1]<<8;
-            section_size |= data[0];
-
-            // Seek past the chunk.
-            if (stream->seek(section_size, SEEK_CUR) == false)
-            {
-                GP_ERROR("Failed to seek past '%s' chunk in wave file.", chunk);
-                return false;
-            }
-        }
-    }
-    return false;
-}
-
-bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
-{
-    GP_ASSERT(stream);
-
-    OggVorbis_File ogg_file;
-    vorbis_info* info;
-    ALenum format;
-    long result;
-    int section;
-    long size = 0;
-
-    stream->rewind();
-
-    ov_callbacks callbacks;
-    callbacks.read_func = readStream;
-    callbacks.seek_func = seekStream;
-    callbacks.close_func = closeStream;
-    callbacks.tell_func = tellStream;
-
-    if ((result = ov_open_callbacks(stream, &ogg_file, NULL, 0, callbacks)) < 0)
-    {
-        GP_ERROR("Failed to open ogg file.");
-        return false;
-    }
-
-    info = ov_info(&ogg_file, -1);
-    GP_ASSERT(info);
-    if (info->channels == 1)
-        format = AL_FORMAT_MONO16;
-    else
-        format = AL_FORMAT_STEREO16;
-
-    // size = #samples * #channels * 2 (for 16 bit).
-    long data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2;
-    char* data = new char[data_size];
-
-    while (size < data_size)
-    {
-        result = ov_read(&ogg_file, data + size, data_size - size, 0, 2, 1, &section);
-        if (result > 0)
-        {
-            size += result;
-        }
-        else if (result < 0)
-        {
-            SAFE_DELETE_ARRAY(data);
-            GP_ERROR("Failed to read ogg file; file is missing data.");
-            return false;
-        }
-        else
-        {
-            break;
-        }
-    }
-    
-    if (size == 0)
-    {
-        SAFE_DELETE_ARRAY(data);
-        GP_ERROR("Filed to read ogg file; unable to read any data.");
-        return false;
-    }
-
-    AL_CHECK( alBufferData(buffer, format, data, data_size, info->rate) );
-
-    SAFE_DELETE_ARRAY(data);
-    ov_clear(&ogg_file);
-
-    return true;
-}
-
-}
+#include "Base.h"
+#include "AudioBuffer.h"
+#include "FileSystem.h"
+
+namespace gameplay
+{
+
+// Audio buffer cache
+static std::vector<AudioBuffer*> __buffers;
+
+// Callbacks for loading an ogg file using Stream
+static size_t readStream(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return stream->read(ptr, size, nmemb);
+}
+
+static int seekStream(void *datasource, ogg_int64_t offset, int whence)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return !stream->seek(offset, whence);
+}
+
+static int closeStream(void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    stream->close();
+    return 0;
+}
+
+static long tellStream(void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return stream->position();
+}
+
+AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
+    : _filePath(path), _alBuffer(buffer)
+{
+}
+
+AudioBuffer::~AudioBuffer()
+{
+    // Remove the buffer from the cache.
+    unsigned int bufferCount = (unsigned int)__buffers.size();
+    for (unsigned int i = 0; i < bufferCount; i++)
+    {
+        if (this == __buffers[i])
+        {
+            __buffers.erase(__buffers.begin() + i);
+            break;
+        }
+    }
+
+    if (_alBuffer)
+    {
+        AL_CHECK( alDeleteBuffers(1, &_alBuffer) );
+        _alBuffer = 0;
+    }
+}
+
+AudioBuffer* AudioBuffer::create(const char* path)
+{
+    GP_ASSERT(path);
+
+    // Search the cache for a stream from this file.
+    unsigned int bufferCount = (unsigned int)__buffers.size();
+    AudioBuffer* buffer = NULL;
+    for (unsigned int i = 0; i < bufferCount; i++)
+    {
+        buffer = __buffers[i];
+        GP_ASSERT(buffer);
+        if (buffer->_filePath.compare(path) == 0)
+        {
+            buffer->addRef();
+            return buffer;
+        }
+    }
+
+    ALuint alBuffer;
+
+    // Load audio data into a buffer.
+    AL_CHECK( alGenBuffers(1, &alBuffer) );
+    if (AL_LAST_ERROR())
+    {
+        GP_ERROR("Failed to create OpenAL buffer; alGenBuffers error: %d", AL_LAST_ERROR());
+        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
+        return NULL;
+    }
+    
+    // Load sound file.
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
+    {
+        GP_ERROR("Failed to load audio file %s.", path);
+        goto cleanup;
+    }
+    
+    // Read the file header
+    char header[12];
+    if (stream->read(header, 1, 12) != 12)
+    {
+        GP_ERROR("Invalid header for audio file %s.", path);
+        goto cleanup;
+    }
+    
+    // Check the file format
+    if (memcmp(header, "RIFF", 4) == 0)
+    {
+        if (!AudioBuffer::loadWav(stream.get(), alBuffer))
+        {
+            GP_ERROR("Invalid wave file: %s", path);
+            goto cleanup;
+        }
+    }
+    else if (memcmp(header, "OggS", 4) == 0)
+    {
+        if (!AudioBuffer::loadOgg(stream.get(), alBuffer))
+        {
+            GP_ERROR("Invalid ogg file: %s", path);
+            goto cleanup;
+        }
+    }
+    else
+    {
+        GP_ERROR("Unsupported audio file: %s", path);
+        goto cleanup;
+    }
+
+    buffer = new AudioBuffer(path, alBuffer);
+
+    // Add the buffer to the cache.
+    __buffers.push_back(buffer);
+
+    return buffer;
+    
+cleanup:
+    if (alBuffer)
+        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
+    return NULL;
+}
+
+bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
+{
+    GP_ASSERT(stream);
+
+    unsigned char data[12];
+    
+    // Verify the wave fmt magic value meaning format.
+    if (stream->read(data, 1, 8) != 8 || memcmp(data, "fmt ", 4) != 0 )
+    {
+        GP_ERROR("Failed to verify the magic value for the wave file format.");
+        return false;
+    }
+    
+    unsigned int section_size;
+    section_size  = data[7]<<24;
+    section_size |= data[6]<<16;
+    section_size |= data[5]<<8;
+    section_size |= data[4];
+
+    // Check for a valid pcm format.
+    if (stream->read(data, 1, 2) != 2 || data[1] != 0 || data[0] != 1)
+    {
+        GP_ERROR("Unsupported audio file format (must be a valid PCM format).");
+        return false;
+    }
+    
+    // Get the channel count (16-bit little-endian).
+    int channels;
+    if (stream->read(data, 1, 2) != 2)
+    {
+        GP_ERROR("Failed to read the wave file's channel count.");
+        return false;
+    }
+    channels  = data[1]<<8;
+    channels |= data[0];
+    
+    // Get the sample frequency (32-bit little-endian).
+    ALuint frequency;
+    if (stream->read(data, 1, 4) != 4)
+    {
+        GP_ERROR("Failed to read the wave file's sample frequency.");
+        return false;
+    }
+
+    frequency  = data[3]<<24;
+    frequency |= data[2]<<16;
+    frequency |= data[1]<<8;
+    frequency |= data[0];
+    
+    // The next 6 bytes hold the block size and bytes-per-second. 
+    // We don't need that info, so just read and ignore it. 
+    // We could use this later if we need to know the duration.
+    if (stream->read(data, 1, 6) != 6)
+    {
+        GP_ERROR("Failed to read past the wave file's block size and bytes-per-second.");
+        return false;
+    }
+    
+    // Get the bit depth (16-bit little-endian).
+    int bits;
+    if (stream->read(data, 1, 2) != 2)
+    {
+        GP_ERROR("Failed to read the wave file's bit depth.");
+        return false;
+    }
+    bits  = data[1]<<8;
+    bits |= data[0];
+    
+    // Now convert the given channel count and bit depth into an OpenAL format. 
+    ALuint format = 0;
+    if (bits == 8)
+    {
+        if (channels == 1)
+            format = AL_FORMAT_MONO8;
+        else if (channels == 2)
+            format = AL_FORMAT_STEREO8;
+    }
+    else if (bits == 16)
+    {
+        if (channels == 1)
+            format = AL_FORMAT_MONO16;
+        else if (channels == 2)
+            format = AL_FORMAT_STEREO16;
+    }
+    else
+    {
+        GP_ERROR("Incompatible wave file format: (%d, %d)", channels, bits);
+        return false;
+    }
+    
+    // Check against the size of the format header as there may be more data that we need to read.
+    if (section_size > 16)
+    {
+        unsigned int length = section_size - 16;
+
+        // Extension size is 2 bytes.
+        if (stream->read(data, 1, length) != length)
+        {
+            GP_ERROR("Failed to read extension size from wave file.");
+            return false;
+        }
+    }
+
+    // Read in the rest of the file a chunk (section) at a time.
+    while (true)
+    {
+        // Check if we are at the end of the file without reading the data.
+        if (stream->eof())
+        {
+            GP_ERROR("Failed to load wave file; file appears to have no data.");
+            return false;
+        }
+
+        // Read in the type of the next section of the file.
+        if (stream->read(data, 1, 4) != 4)
+        {
+            GP_ERROR("Failed to read next section type from wave file.");
+            return false;
+        }
+
+        // Data chunk.
+        if (memcmp(data, "data", 4) == 0)
+        {
+            // Read how much data is remaining and buffer it up.
+            unsigned int dataSize;
+            if (stream->read(&dataSize, sizeof(int), 1) != 1)
+            {
+                GP_ERROR("Failed to read size of data section from wave file.");
+                return false;
+            }
+
+            char* data = new char[dataSize];
+            if (stream->read(data, sizeof(char), dataSize) != dataSize)
+            {
+                GP_ERROR("Failed to load wave file; file is missing data.");
+                SAFE_DELETE_ARRAY(data);
+                return false;
+            }
+
+            AL_CHECK( alBufferData(buffer, format, data, dataSize, frequency) );
+            SAFE_DELETE_ARRAY(data);
+
+            // We've read the data, so return now.
+            return true;
+        }
+        // Other chunk - could be any of the following:
+        // - Fact ("fact")
+        // - Wave List ("wavl")
+        // - Silent ("slnt")
+        // - Cue ("cue ")
+        // - Playlist ("plst")
+        // - Associated Data List ("list")
+        // - Label ("labl")
+        // - Note ("note")
+        // - Labeled Text ("ltxt")
+        // - Sampler ("smpl")
+        // - Instrument ("inst")
+        else
+        {
+            // Store the name of the chunk so we can report errors informatively.
+            char chunk[5] = { 0 };
+            memcpy(chunk, data, 4);
+
+            // Read the chunk size.
+            if (stream->read(data, 1, 4) != 4)
+            {
+                GP_ERROR("Failed to read size of '%s' chunk from wave file.", chunk);
+                return false;
+            }
+
+            section_size  = data[3]<<24;
+            section_size |= data[2]<<16;
+            section_size |= data[1]<<8;
+            section_size |= data[0];
+
+            // Seek past the chunk.
+            if (stream->seek(section_size, SEEK_CUR) == false)
+            {
+                GP_ERROR("Failed to seek past '%s' chunk in wave file.", chunk);
+                return false;
+            }
+        }
+    }
+    return false;
+}
+
+bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
+{
+    GP_ASSERT(stream);
+
+    OggVorbis_File ogg_file;
+    vorbis_info* info;
+    ALenum format;
+    long result;
+    int section;
+    long size = 0;
+
+    stream->rewind();
+
+    ov_callbacks callbacks;
+    callbacks.read_func = readStream;
+    callbacks.seek_func = seekStream;
+    callbacks.close_func = closeStream;
+    callbacks.tell_func = tellStream;
+
+    if ((result = ov_open_callbacks(stream, &ogg_file, NULL, 0, callbacks)) < 0)
+    {
+        GP_ERROR("Failed to open ogg file.");
+        return false;
+    }
+
+    info = ov_info(&ogg_file, -1);
+    GP_ASSERT(info);
+    if (info->channels == 1)
+        format = AL_FORMAT_MONO16;
+    else
+        format = AL_FORMAT_STEREO16;
+
+    // size = #samples * #channels * 2 (for 16 bit).
+    long data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2;
+    char* data = new char[data_size];
+
+    while (size < data_size)
+    {
+        result = ov_read(&ogg_file, data + size, data_size - size, 0, 2, 1, &section);
+        if (result > 0)
+        {
+            size += result;
+        }
+        else if (result < 0)
+        {
+            SAFE_DELETE_ARRAY(data);
+            GP_ERROR("Failed to read ogg file; file is missing data.");
+            return false;
+        }
+        else
+        {
+            break;
+        }
+    }
+    
+    if (size == 0)
+    {
+        SAFE_DELETE_ARRAY(data);
+        GP_ERROR("Filed to read ogg file; unable to read any data.");
+        return false;
+    }
+
+    AL_CHECK( alBufferData(buffer, format, data, data_size, info->rate) );
+
+    SAFE_DELETE_ARRAY(data);
+    ov_clear(&ogg_file);
+
+    return true;
+}
+
+}

+ 57 - 57
gameplay/src/AudioBuffer.h

@@ -1,57 +1,57 @@
-#ifndef AUDIOBUFFER_H_
-#define AUDIOBUFFER_H_
-
-#include "Ref.h"
-#include "Stream.h"
-
-namespace gameplay
-{
-
-class AudioSource;
-
-/**
- * The actual audio buffer data.
- *
- * Currently only supports supported formats: .wav, .au and .raw files.
- */
-class AudioBuffer : public Ref
-{
-    friend class AudioSource;
-
-private:
-    
-    /**
-     * Constructor.
-     */
-    AudioBuffer(const char* path, ALuint buffer);
-
-    /**
-     * Destructor.
-     */
-    virtual ~AudioBuffer();
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    AudioBuffer& operator=(const AudioBuffer&);
-
-    /**
-     * Creates an audio buffer from a file.
-     * 
-     * @param path The path to the audio buffer on the filesystem.
-     * 
-     * @return The buffer from a file.
-     */
-    static AudioBuffer* create(const char* path);
-    
-    static bool loadWav(Stream* stream, ALuint buffer);
-    
-    static bool loadOgg(Stream* stream, ALuint buffer);
-
-    std::string _filePath;
-    ALuint _alBuffer;
-};
-
-}
-
-#endif
+#ifndef AUDIOBUFFER_H_
+#define AUDIOBUFFER_H_
+
+#include "Ref.h"
+#include "Stream.h"
+
+namespace gameplay
+{
+
+class AudioSource;
+
+/**
+ * Defines the actual audio buffer data.
+ *
+ * Currently only supports supported formats: .ogg, .wav, .au and .raw files.
+ */
+class AudioBuffer : public Ref
+{
+    friend class AudioSource;
+
+private:
+    
+    /**
+     * Constructor.
+     */
+    AudioBuffer(const char* path, ALuint buffer);
+
+    /**
+     * Destructor.
+     */
+    virtual ~AudioBuffer();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    AudioBuffer& operator=(const AudioBuffer&);
+
+    /**
+     * Creates an audio buffer from a file.
+     * 
+     * @param path The path to the audio buffer on the filesystem.
+     * 
+     * @return The buffer from a file.
+     */
+    static AudioBuffer* create(const char* path);
+    
+    static bool loadWav(Stream* stream, ALuint buffer);
+    
+    static bool loadOgg(Stream* stream, ALuint buffer);
+
+    std::string _filePath;
+    ALuint _alBuffer;
+};
+
+}
+
+#endif

+ 106 - 106
gameplay/src/AudioController.cpp

@@ -1,106 +1,106 @@
-#include "Base.h"
-#include "AudioController.h"
-#include "AudioListener.h"
-#include "AudioBuffer.h"
-#include "AudioSource.h"
-
-namespace gameplay
-{
-
-AudioController::AudioController() 
-    : _alcDevice(NULL), _alcContext(NULL), _pausingSource(NULL)
-{
-}
-
-AudioController::~AudioController()
-{
-}
-
-void AudioController::initialize()
-{
-    _alcDevice = alcOpenDevice(NULL);
-    if (!_alcDevice)
-    {
-        GP_ERROR("Unable to open OpenAL device.\n");
-        return;
-    }
-    
-    _alcContext = alcCreateContext(_alcDevice, NULL);
-    ALCenum alcErr = alcGetError(_alcDevice);
-    if (!_alcContext || alcErr != ALC_NO_ERROR)
-    {
-        alcCloseDevice(_alcDevice);
-        GP_ERROR("Unable to create OpenAL context. Error: %d\n", alcErr);
-        return;
-    }
-    
-    alcMakeContextCurrent(_alcContext);
-    alcErr = alcGetError(_alcDevice);
-    if (alcErr != ALC_NO_ERROR)
-    {
-        GP_ERROR("Unable to make OpenAL context current. Error: %d\n", alcErr);
-    }
-}
-
-void AudioController::finalize()
-{
-    alcMakeContextCurrent(NULL);
-    if (_alcContext)
-    {
-        alcDestroyContext(_alcContext);
-        _alcContext = NULL;
-    }
-    if (_alcDevice)
-    {
-        alcCloseDevice(_alcDevice);
-        _alcDevice = NULL;
-    }
-}
-
-void AudioController::pause()
-{
-    std::set<AudioSource*>::iterator itr = _playingSources.begin();
-
-    // For each source that is playing, pause it.
-    AudioSource* source = NULL;
-    while (itr != _playingSources.end())
-    {
-        GP_ASSERT(*itr);
-        source = *itr;
-        _pausingSource = source;
-        source->pause();
-        _pausingSource = NULL;
-        itr++;
-    }
-}
-
-void AudioController::resume()
-{   
-    alcMakeContextCurrent(_alcContext);
-
-    std::set<AudioSource*>::iterator itr = _playingSources.begin();
-
-    // For each source that is playing, resume it.
-    AudioSource* source = NULL;
-    while (itr != _playingSources.end())
-    {
-        GP_ASSERT(*itr);
-        source = *itr;
-        source->resume();
-        itr++;
-    }
-}
-
-void AudioController::update(float elapsedTime)
-{
-    AudioListener* listener = AudioListener::getInstance();
-    if (listener)
-    {
-        AL_CHECK( alListenerf(AL_GAIN, listener->getGain()) );
-        AL_CHECK( alListenerfv(AL_ORIENTATION, (ALfloat*)listener->getOrientation()) );
-        AL_CHECK( alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity()) );
-        AL_CHECK( alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition()) );
-    }
-}
-
-}
+#include "Base.h"
+#include "AudioController.h"
+#include "AudioListener.h"
+#include "AudioBuffer.h"
+#include "AudioSource.h"
+
+namespace gameplay
+{
+
+AudioController::AudioController() 
+    : _alcDevice(NULL), _alcContext(NULL), _pausingSource(NULL)
+{
+}
+
+AudioController::~AudioController()
+{
+}
+
+void AudioController::initialize()
+{
+    _alcDevice = alcOpenDevice(NULL);
+    if (!_alcDevice)
+    {
+        GP_ERROR("Unable to open OpenAL device.\n");
+        return;
+    }
+    
+    _alcContext = alcCreateContext(_alcDevice, NULL);
+    ALCenum alcErr = alcGetError(_alcDevice);
+    if (!_alcContext || alcErr != ALC_NO_ERROR)
+    {
+        alcCloseDevice(_alcDevice);
+        GP_ERROR("Unable to create OpenAL context. Error: %d\n", alcErr);
+        return;
+    }
+    
+    alcMakeContextCurrent(_alcContext);
+    alcErr = alcGetError(_alcDevice);
+    if (alcErr != ALC_NO_ERROR)
+    {
+        GP_ERROR("Unable to make OpenAL context current. Error: %d\n", alcErr);
+    }
+}
+
+void AudioController::finalize()
+{
+    alcMakeContextCurrent(NULL);
+    if (_alcContext)
+    {
+        alcDestroyContext(_alcContext);
+        _alcContext = NULL;
+    }
+    if (_alcDevice)
+    {
+        alcCloseDevice(_alcDevice);
+        _alcDevice = NULL;
+    }
+}
+
+void AudioController::pause()
+{
+    std::set<AudioSource*>::iterator itr = _playingSources.begin();
+
+    // For each source that is playing, pause it.
+    AudioSource* source = NULL;
+    while (itr != _playingSources.end())
+    {
+        GP_ASSERT(*itr);
+        source = *itr;
+        _pausingSource = source;
+        source->pause();
+        _pausingSource = NULL;
+        itr++;
+    }
+}
+
+void AudioController::resume()
+{   
+    alcMakeContextCurrent(_alcContext);
+
+    std::set<AudioSource*>::iterator itr = _playingSources.begin();
+
+    // For each source that is playing, resume it.
+    AudioSource* source = NULL;
+    while (itr != _playingSources.end())
+    {
+        GP_ASSERT(*itr);
+        source = *itr;
+        source->resume();
+        itr++;
+    }
+}
+
+void AudioController::update(float elapsedTime)
+{
+    AudioListener* listener = AudioListener::getInstance();
+    if (listener)
+    {
+        AL_CHECK( alListenerf(AL_GAIN, listener->getGain()) );
+        AL_CHECK( alListenerfv(AL_ORIENTATION, (ALfloat*)listener->getOrientation()) );
+        AL_CHECK( alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity()) );
+        AL_CHECK( alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition()) );
+    }
+}
+
+}

+ 66 - 66
gameplay/src/AudioController.h

@@ -1,66 +1,66 @@
-#ifndef AUDIOCONTROLLER_H_
-#define AUDIOCONTROLLER_H_
-
-namespace gameplay
-{
-
-class AudioListener;
-class AudioSource;
-
-/**
- * Defines a class for controlling game audio.
- */
-class AudioController
-{
-    friend class Game;
-    friend class AudioSource;
-
-public:
-    
-    /**
-     * Destructor.
-     */
-    virtual ~AudioController();
-
-private:
-    
-    /**
-     * Constructor.
-     */
-    AudioController();
-
-    /**
-     * Controller initialize.
-     */
-    void initialize();
-
-    /**
-     * Controller finalize.
-     */
-    void finalize();
-
-    /**
-     * Controller pause.
-     */
-    void pause();
-
-    /**
-     * Controller resume.
-     */
-    void resume();
-
-    /**
-     * Controller update.
-     */
-    void update(float elapsedTime);
-
-
-    ALCdevice* _alcDevice;
-    ALCcontext* _alcContext;
-    std::set<AudioSource*> _playingSources;
-    AudioSource* _pausingSource;
-};
-
-}
-
-#endif
+#ifndef AUDIOCONTROLLER_H_
+#define AUDIOCONTROLLER_H_
+
+namespace gameplay
+{
+
+class AudioListener;
+class AudioSource;
+
+/**
+ * Defines a class for controlling game audio.
+ */
+class AudioController
+{
+    friend class Game;
+    friend class AudioSource;
+
+public:
+    
+    /**
+     * Destructor.
+     */
+    virtual ~AudioController();
+
+private:
+    
+    /**
+     * Constructor.
+     */
+    AudioController();
+
+    /**
+     * Controller initialize.
+     */
+    void initialize();
+
+    /**
+     * Controller finalize.
+     */
+    void finalize();
+
+    /**
+     * Controller pause.
+     */
+    void pause();
+
+    /**
+     * Controller resume.
+     */
+    void resume();
+
+    /**
+     * Controller update.
+     */
+    void update(float elapsedTime);
+
+
+    ALCdevice* _alcDevice;
+    ALCcontext* _alcContext;
+    std::set<AudioSource*> _playingSources;
+    AudioSource* _pausingSource;
+};
+
+}
+
+#endif

+ 147 - 140
gameplay/src/AudioListener.cpp

@@ -1,140 +1,147 @@
-#include "Base.h"
-#include "Node.h"
-#include "AudioListener.h"
-#include "Game.h"
-
-namespace gameplay
-{
-
-AudioListener::AudioListener()
-    : _gain(1.0f), _camera(NULL)
-{
-}
-
-AudioListener::~AudioListener()
-{
-	// Call setCamera() to release camera and cause transform listener
-	// to be removed.
-	setCamera(NULL);
-}
-
-AudioListener* AudioListener::getInstance()
-{
-    return Game::getInstance()->getAudioListener();
-}
-
-float AudioListener::getGain() const 
-{ 
-    return _gain; 
-}
-
-void AudioListener::setGain(float gain)
-{
-    _gain = gain;
-}
-
-const Vector3& AudioListener::getPosition() const 
-{ 
-    return _position; 
-}
-
-void AudioListener::setPosition(const Vector3& position)
-{
-    _position = position;
-}
-
-void AudioListener::setPosition(float x, float y, float z)
-{
-    _position.set(x, y, z);
-}
-
-const Vector3& AudioListener::getVelocity() const 
-{ 
-    return _velocity; 
-}
-
-void AudioListener::setVelocity(const Vector3& velocity)
-{
-    _velocity = velocity;
-}
-
-void AudioListener::setVelocity(float x, float y, float z)
-{
-    _velocity.set(x, y, z);
-}
-
-const float* AudioListener::getOrientation() const
-{
-    return (const float*)&_orientation[0];
-}
-
-const Vector3& AudioListener::getOrientationForward() const 
-{ 
-    return _orientation[0]; 
-}
-
-const Vector3& AudioListener::getOrientationUp() const 
-{ 
-    return _orientation[1]; 
-}
-
-void AudioListener::setOrientation(const Vector3& forward, const Vector3& up)
-{
-    _orientation[0].x = forward.x;
-    _orientation[0].y = forward.y;
-    _orientation[0].z = forward.z;
-
-    _orientation[1].x = up.x;
-    _orientation[1].y = up.y;
-    _orientation[1].z = up.z;
-}
-
-void AudioListener::setOrientation(float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ)
-{
-    _orientation[0].set(forwardX, forwardY, forwardZ);
-    _orientation[1].set(upX, upY, upZ);
-}
-
-Camera* AudioListener::getCamera() const
-{
-    return _camera;
-}
-
-void AudioListener::setCamera(Camera* c)
-{
-    if (_camera != c)
-    {
-        // Disconnect our current camera.
-        if (_camera)
-        {
-            GP_ASSERT(_camera->getNode());
-            _camera->getNode()->removeListener(this);
-            SAFE_RELEASE(_camera);
-        }
-
-        // Connect the new camera.
-        _camera = c;
-
-        if (_camera)
-        {
-            GP_ASSERT(_camera->getNode());
-            _camera->addRef();
-            _camera->getNode()->addListener(this);
-        }
-    }
-}
-
-void AudioListener::transformChanged(Transform* transform, long cookie)
-{
-    if (transform)
-    {
-        Node* node = static_cast<Node*>(transform);
-        setPosition(node->getTranslationWorld());
-        
-        Vector3 up;
-        node->getWorldMatrix().getUpVector(&up);
-        setOrientation(node->getForwardVectorWorld(), up);
-    }
-}
-
-}
+#include "Base.h"
+#include "Node.h"
+#include "AudioListener.h"
+#include "Game.h"
+
+namespace gameplay
+{
+
+AudioListener::AudioListener()
+    : _gain(1.0f), _camera(NULL)
+{
+}
+
+AudioListener::~AudioListener()
+{
+	// Call setCamera() to release camera and cause transform listener
+	// to be removed.
+	setCamera(NULL);
+}
+
+AudioListener* AudioListener::getInstance()
+{
+    return Game::getInstance()->getAudioListener();
+}
+
+float AudioListener::getGain() const 
+{ 
+    return _gain; 
+}
+
+void AudioListener::setGain(float gain)
+{
+    _gain = gain;
+}
+
+const Vector3& AudioListener::getPosition() const 
+{ 
+    return _position; 
+}
+
+void AudioListener::setPosition(const Vector3& position)
+{
+    _position = position;
+}
+
+void AudioListener::setPosition(float x, float y, float z)
+{
+    _position.set(x, y, z);
+}
+
+const Vector3& AudioListener::getVelocity() const 
+{ 
+    return _velocity; 
+}
+
+void AudioListener::setVelocity(const Vector3& velocity)
+{
+    _velocity = velocity;
+}
+
+void AudioListener::setVelocity(float x, float y, float z)
+{
+    _velocity.set(x, y, z);
+}
+
+const float* AudioListener::getOrientation() const
+{
+    return (const float*)&_orientation[0];
+}
+
+const Vector3& AudioListener::getOrientationForward() const 
+{ 
+    return _orientation[0]; 
+}
+
+const Vector3& AudioListener::getOrientationUp() const 
+{ 
+    return _orientation[1]; 
+}
+
+void AudioListener::setOrientation(const Vector3& forward, const Vector3& up)
+{
+    _orientation[0].x = forward.x;
+    _orientation[0].y = forward.y;
+    _orientation[0].z = forward.z;
+
+    _orientation[1].x = up.x;
+    _orientation[1].y = up.y;
+    _orientation[1].z = up.z;
+}
+
+void AudioListener::setOrientation(float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ)
+{
+    _orientation[0].set(forwardX, forwardY, forwardZ);
+    _orientation[1].set(upX, upY, upZ);
+}
+
+Camera* AudioListener::getCamera() const
+{
+    return _camera;
+}
+
+void AudioListener::setCamera(Camera* camera)
+{
+    if (_camera == camera)
+        return;
+
+    // Disconnect our current camera.
+    if (_camera)
+    {
+        _camera->removeListener(this);
+        SAFE_RELEASE(_camera);
+    }
+
+    // Connect the new camera.
+    _camera = camera;
+    if (_camera)
+    {
+        _camera->addRef();
+        _camera->addListener(this);
+    }
+}
+
+void AudioListener::cameraChanged(Camera* camera)
+{
+    if (_camera != camera)
+        setCamera(camera);
+   
+    if (_camera)
+    {
+        Node* node = camera->getNode();
+        if (node)
+        {
+            setPosition(node->getTranslationWorld());
+            Vector3 up;
+            node->getWorldMatrix().getUpVector(&up);
+            setOrientation(node->getForwardVectorWorld(), up);
+        }
+        else
+        {
+            setPosition(Vector3::zero());
+            setOrientation(Vector3::unitY(), -Vector3::unitZ());
+        }
+    }
+}
+
+}

+ 176 - 172
gameplay/src/AudioListener.h

@@ -1,172 +1,176 @@
-#ifndef AUDIOLISTENER_H_
-#define AUDIOLISTENER_H_
-
-#include "Vector3.h"
-#include "Transform.h"
-
-namespace gameplay
-{
-
-class Camera;
-
-/**
- * Defines an audio listener in 3D space.
- */
-class AudioListener : public Transform::Listener
-{
-    friend class AudioController;
-    friend class Game;
-
-public:
-
-    /**
-     * Gets the single instance of the audio listener.
-     *
-     * @return The single instance of the audio listener.
-     */
-    static AudioListener* getInstance();
-
-    /**
-     * Gets the current position of the audio listener.
-     *
-     * @return The position of the audio listener
-     */
-    const Vector3& getPosition() const;
-
-    /**
-     * Sets the position of the audio source.
-     *
-     * @param position The position to set the listener to.
-     */
-    void setPosition(const Vector3& position);
-
-    /**
-     * Sets the position of the audio source.
-     * 
-     * @param x The x coordinate of the position.
-     * @param y The y coordinate of the position.
-     * @param z The z coordinate of the position.
-     */
-    void setPosition(float x, float y, float z);
-
-    /**
-     * Returns the gain of the audio listener.
-     *
-     * @return The gain of the audio listener.
-     */
-    float getGain() const;
-
-    /**
-     * Sets the gain/volume of the audio listener.
-     *
-     * @param gain The gain/volume of the listener.
-     */
-    void setGain(float gain);
-
-    /**
-     * Gets the velocity of the audio source.
-     *
-     * @return The velocity as a vector.
-     */
-    const Vector3& getVelocity() const;
-
-    /**
-     * Sets the velocity of the audio source
-     *
-     * @param velocity A vector representing the velocity.
-     */
-    void setVelocity(const Vector3& velocity);
-
-    /**
-     * Sets the velocity of the audio source
-     * 
-     * @param x The x coordinate of the velocity.
-     * @param y The y coordinate of the velocity.
-     * @param z The z coordinate of the velocity.
-     */
-    void setVelocity(float x, float y, float z);
-
-    /**
-     * Gets the float pointer to the orientation of the audio listener.
-     * Orientation is represented as 6 floats. (forward.x, forward.y, forward.z, up.x, up.y, up.z).
-     * 
-     * @return Pointer to the 6 orientation float values.
-     * @script{ignore}
-     */
-    const float* getOrientation() const;
-
-    /**
-     * Gets the forward orientation vector of the audio listener.
-     *
-     * @return The forward vector.
-     */
-    const Vector3& getOrientationForward() const;
-
-    /**
-     * Gets the up orientation vector of the audio listener.
-     *
-     * @return The up vector.
-     */
-    const Vector3& getOrientationUp() const;
-
-    /**
-     * Sets the orientation of the audio listener.
-     *
-     * @param forward The forward vector.
-     * @param up The up vector.
-     */
-    void setOrientation(const Vector3& forward, const Vector3& up);
-
-    /**
-     * Sets the orientation of the audio listener.
-     * 
-     * @param forwardX The x coordinate of the forward vector.
-     * @param forwardY The y coordinate of the forward vector.
-     * @param forwardZ The z coordinate of the forward vector.
-     * @param upX The x coordinate of the up vector.
-     * @param upY The y coordinate of the up vector.
-     * @param upZ The z coordinate of the up vector.
-     */
-    void setOrientation(float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ);
-
-    /**
-     * Gets the camera currently associated with the audio listener.
-     *
-     * @return camera The camera currently associated with the audio listener.
-     */
-    Camera* getCamera() const;
-
-    /**
-     * Sets the camera that is associated with the audio listener. This should usually be the current camera.
-     *
-     * @param camera The camera that is associated with the audio listener
-     */
-    void setCamera(Camera* camera);
-
-private:
-
-    /**
-    * Constructor.
-    */
-    AudioListener();
-
-    /**
-    * Destructor.
-    */
-    ~AudioListener();
-
-    /**
-    * @see Transform::Listener::transformChanged
-    */
-    void transformChanged(Transform* transform, long cookie);
-
-    float _gain;
-    Vector3 _position;
-    Vector3 _velocity;
-    Vector3 _orientation[2];
-    Camera* _camera;
-};
-
-}
-
-#endif
+#ifndef AUDIOLISTENER_H_
+#define AUDIOLISTENER_H_
+
+#include "Vector3.h"
+#include "Transform.h"
+#include "Camera.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines an audio listener in 3D space.
+ *
+ * The audio listener represents where you are listening from.
+ * In a 3D scene this is usually the active camera.
+ * 
+ * @see Scene::bindAudioListenerToCamera
+ */
+class AudioListener : public Camera::Listener
+{
+    friend class AudioController;
+    friend class Game;
+
+public:
+
+    /**
+     * Gets the single instance of the audio listener.
+     *
+     * @return The single instance of the audio listener.
+     */
+    static AudioListener* getInstance();
+
+    /**
+     * Gets the current position of the audio listener.
+     *
+     * @return The position of the audio listener
+     */
+    const Vector3& getPosition() const;
+
+    /**
+     * Sets the position of the audio source.
+     *
+     * @param position The position to set the listener to.
+     */
+    void setPosition(const Vector3& position);
+
+    /**
+     * Sets the position of the audio source.
+     * 
+     * @param x The x coordinate of the position.
+     * @param y The y coordinate of the position.
+     * @param z The z coordinate of the position.
+     */
+    void setPosition(float x, float y, float z);
+
+    /**
+     * Returns the gain of the audio listener.
+     *
+     * @return The gain of the audio listener.
+     */
+    float getGain() const;
+
+    /**
+     * Sets the gain/volume of the audio listener.
+     *
+     * @param gain The gain/volume of the listener.
+     */
+    void setGain(float gain);
+
+    /**
+     * Gets the velocity of the audio source.
+     *
+     * @return The velocity as a vector.
+     */
+    const Vector3& getVelocity() const;
+
+    /**
+     * Sets the velocity of the audio source
+     *
+     * @param velocity A vector representing the velocity.
+     */
+    void setVelocity(const Vector3& velocity);
+
+    /**
+     * Sets the velocity of the audio source
+     * 
+     * @param x The x coordinate of the velocity.
+     * @param y The y coordinate of the velocity.
+     * @param z The z coordinate of the velocity.
+     */
+    void setVelocity(float x, float y, float z);
+
+    /**
+     * Gets the float pointer to the orientation of the audio listener.
+     * Orientation is represented as 6 floats. (forward.x, forward.y, forward.z, up.x, up.y, up.z).
+     * 
+     * @return Pointer to the 6 orientation float values.
+     * @script{ignore}
+     */
+    const float* getOrientation() const;
+
+    /**
+     * Gets the forward orientation vector of the audio listener.
+     *
+     * @return The forward vector.
+     */
+    const Vector3& getOrientationForward() const;
+
+    /**
+     * Gets the up orientation vector of the audio listener.
+     *
+     * @return The up vector.
+     */
+    const Vector3& getOrientationUp() const;
+
+    /**
+     * Sets the orientation of the audio listener.
+     *
+     * @param forward The forward vector.
+     * @param up The up vector.
+     */
+    void setOrientation(const Vector3& forward, const Vector3& up);
+
+    /**
+     * Sets the orientation of the audio listener.
+     * 
+     * @param forwardX The x coordinate of the forward vector.
+     * @param forwardY The y coordinate of the forward vector.
+     * @param forwardZ The z coordinate of the forward vector.
+     * @param upX The x coordinate of the up vector.
+     * @param upY The y coordinate of the up vector.
+     * @param upZ The z coordinate of the up vector.
+     */
+    void setOrientation(float forwardX, float forwardY, float forwardZ, float upX, float upY, float upZ);
+
+    /**
+     * Gets the camera currently associated with the audio listener.
+     *
+     * @return camera The camera currently associated with the audio listener.
+     */
+    Camera* getCamera() const;
+
+    /**
+     * Sets the camera that is associated with the audio listener. This should usually be the current camera.
+     *
+     * @param camera The camera that is associated with the audio listener
+     */
+    void setCamera(Camera* camera);
+
+private:
+
+    /**
+    * Constructor.
+    */
+    AudioListener();
+
+    /**
+    * Destructor.
+    */
+    ~AudioListener();
+
+    /**
+    * @see Camera::Listener::cameraChanged
+    */
+    void cameraChanged(Camera* camera);
+
+    float _gain;
+    Vector3 _position;
+    Vector3 _velocity;
+    Vector3 _orientation[2];
+    Camera* _camera;
+};
+
+}
+
+#endif

+ 306 - 306
gameplay/src/AudioSource.cpp

@@ -1,306 +1,306 @@
-#include "Base.h"
-#include "Node.h"
-#include "AudioBuffer.h"
-#include "AudioController.h"
-#include "AudioSource.h"
-#include "Game.h"
-#include "Node.h"
-
-namespace gameplay
-{
-
-AudioSource::AudioSource(AudioBuffer* buffer, ALuint source) 
-    : _alSource(source), _buffer(buffer), _looped(false), _gain(1.0f), _pitch(1.0f), _node(NULL)
-{
-    GP_ASSERT(buffer);
-    AL_CHECK( alSourcei(_alSource, AL_BUFFER, buffer->_alBuffer) );
-    AL_CHECK( alSourcei(_alSource, AL_LOOPING, _looped) );
-    AL_CHECK( alSourcef(_alSource, AL_PITCH, _pitch) );
-    AL_CHECK( alSourcef(_alSource, AL_GAIN, _gain) );
-    AL_CHECK( alSourcefv(_alSource, AL_VELOCITY, (const ALfloat*)&_velocity) );
-}
-
-AudioSource::~AudioSource()
-{
-    if (_alSource)
-    {
-        AL_CHECK( alDeleteSources(1, &_alSource) );
-        _alSource = 0;
-    }
-    SAFE_RELEASE(_buffer);
-}
-
-AudioSource* AudioSource::create(const char* url)
-{
-    // Load from a .audio file.
-    std::string pathStr = url;
-    if (pathStr.find(".audio") != std::string::npos)
-    {
-        Properties* properties = Properties::create(url);
-        if (properties == NULL)
-        {
-            GP_ERROR("Failed to create audio source from .audio file.");
-            return NULL;
-        }
-
-        AudioSource* audioSource = create((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
-        SAFE_DELETE(properties);
-        return audioSource;
-    }
-
-    // Create an audio buffer from this URL.
-    AudioBuffer* buffer = AudioBuffer::create(url);
-    if (buffer == NULL)
-        return NULL;
-
-    // Load the audio source.
-    ALuint alSource = 0;
-
-    AL_CHECK( alGenSources(1, &alSource) );
-    if (AL_LAST_ERROR())
-    {
-        SAFE_RELEASE(buffer);
-        GP_ERROR("Error generating audio source.");
-        return NULL;
-    }
-    
-    return new AudioSource(buffer, alSource);
-}
-
-AudioSource* AudioSource::create(Properties* properties)
-{
-    // Check if the properties is valid and has a valid namespace.
-    GP_ASSERT(properties);
-    if (!properties || !(strcmp(properties->getNamespace(), "audio") == 0))
-    {
-        GP_ERROR("Failed to load audio source from properties object: must be non-null object and have namespace equal to 'audio'.");
-        return 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.c_str());
-    if (audio == NULL)
-    {
-        GP_ERROR("Audio file '%s' failed to load properly.", path.c_str());
-        return NULL;
-    }
-
-    // Set any properties that the user specified in the .audio file.
-    if (properties->exists("looped"))
-    {
-        audio->setLooped(properties->getBool("looped"));
-    }
-    if (properties->exists("gain"))
-    {
-        audio->setGain(properties->getFloat("gain"));
-    }
-    if (properties->exists("pitch"))
-    {
-        audio->setPitch(properties->getFloat("pitch"));
-    }
-    Vector3 v;
-    if (properties->getVector3("velocity", &v))
-    {
-        audio->setVelocity(v);
-    }
-
-    return audio;
-}
-
-AudioSource::State AudioSource::getState() const
-{
-    ALint state;
-    AL_CHECK( alGetSourcei(_alSource, AL_SOURCE_STATE, &state) );
-
-    switch (state)
-    {
-        case AL_PLAYING: 
-            return PLAYING;
-        case AL_PAUSED:  
-            return PAUSED;
-        case AL_STOPPED: 
-            return STOPPED;
-        default:         
-            return INITIAL;
-    }
-    return INITIAL;
-}
-
-void AudioSource::play()
-{
-    AL_CHECK( alSourcePlay(_alSource) );
-
-    // Add the source to the controller's list of currently playing sources.
-    AudioController* audioController = Game::getInstance()->getAudioController();
-    GP_ASSERT(audioController);
-    if (audioController->_playingSources.find(this) == audioController->_playingSources.end())
-        audioController->_playingSources.insert(this);
-}
-
-void AudioSource::pause()
-{
-    AL_CHECK( alSourcePause(_alSource) );
-
-    // Remove the source from the controller's set of currently playing sources
-    // if the source is being paused by the user and not the controller itself.
-    AudioController* audioController = Game::getInstance()->getAudioController();
-    GP_ASSERT(audioController);
-    if (audioController->_pausingSource != this)
-    {
-        std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
-        if (iter != audioController->_playingSources.end())
-            audioController->_playingSources.erase(iter);
-    }
-}
-
-void AudioSource::resume()
-{
-    if (getState() == PAUSED)
-    {
-        play();
-    }
-}
-
-void AudioSource::stop()
-{
-    AL_CHECK( alSourceStop(_alSource) );
-
-    // Remove the source from the controller's set of currently playing sources.
-    AudioController* audioController = Game::getInstance()->getAudioController();
-    GP_ASSERT(audioController);
-    std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
-    if (iter != audioController->_playingSources.end())
-        audioController->_playingSources.erase(iter);
-}
-
-void AudioSource::rewind()
-{
-    AL_CHECK( alSourceRewind(_alSource) );
-}
-
-bool AudioSource::isLooped() const
-{
-    return _looped;
-}
-
-void AudioSource::setLooped(bool looped)
-{
-    AL_CHECK( alSourcei(_alSource, AL_LOOPING, (looped) ? AL_TRUE : AL_FALSE) );
-    if (AL_LAST_ERROR())
-    {
-        GP_ERROR("Failed to set audio source's looped attribute with error: %d", AL_LAST_ERROR());
-    }
-    _looped = looped;
-}
-
-float AudioSource::getGain() const
-{
-    return _gain;
-}
-
-void AudioSource::setGain(float gain)
-{
-    AL_CHECK( alSourcef(_alSource, AL_GAIN, gain) );
-    _gain = gain;
-}
-
-float AudioSource::getPitch() const
-{
-    return _pitch;
-}
-
-void AudioSource::setPitch(float pitch)
-{
-    AL_CHECK( alSourcef(_alSource, AL_PITCH, pitch) );
-    _pitch = pitch;
-}
-
-const Vector3& AudioSource::getVelocity() const
-{
-    return _velocity;
-}
-
-void AudioSource::setVelocity(const Vector3& velocity)
-{
-    AL_CHECK( alSourcefv(_alSource, AL_VELOCITY, (ALfloat*)&velocity) );
-    _velocity = velocity;
-}
-
-void AudioSource::setVelocity(float x, float y, float z)
-{
-    setVelocity(Vector3(x, y, z));
-}
-
-Node* AudioSource::getNode() const
-{
-    return _node;
-}
-
-void AudioSource::setNode(Node* node)
-{
-    if (_node != node)
-    {
-        // Disconnect our current transform.
-        if (_node)
-        {
-            _node->removeListener(this);
-        }
-
-        // Connect the new node.
-        _node = node;
-
-        if (_node)
-        {
-            _node->addListener(this);
-            // Update the audio source position.
-            transformChanged(_node, 0);
-        }
-    }
-}
-
-void AudioSource::transformChanged(Transform* transform, long cookie)
-{
-    if (_node)
-    {
-        Vector3 translation = _node->getTranslationWorld();
-        AL_CHECK( alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&translation.x) );
-    }
-}
-
-AudioSource* AudioSource::clone(NodeCloneContext &context) const
-{
-    GP_ASSERT(_buffer);
-
-    ALuint alSource = 0;
-    AL_CHECK( alGenSources(1, &alSource) );
-    if (AL_LAST_ERROR())
-    {
-        GP_ERROR("Error generating audio source.");
-        return NULL;
-    }
-    AudioSource* audioClone = new AudioSource(_buffer, alSource);
-
-    _buffer->addRef();
-    audioClone->setLooped(isLooped());
-    audioClone->setGain(getGain());
-    audioClone->setPitch(getPitch());
-    audioClone->setVelocity(getVelocity());
-    if (Node* node = getNode())
-    {
-        Node* clonedNode = context.findClonedNode(node);
-        if (clonedNode)
-        {
-            audioClone->setNode(clonedNode);
-        }
-    }
-    return audioClone;
-}
-
-}
+#include "Base.h"
+#include "Node.h"
+#include "AudioBuffer.h"
+#include "AudioController.h"
+#include "AudioSource.h"
+#include "Game.h"
+#include "Node.h"
+
+namespace gameplay
+{
+
+AudioSource::AudioSource(AudioBuffer* buffer, ALuint source) 
+    : _alSource(source), _buffer(buffer), _looped(false), _gain(1.0f), _pitch(1.0f), _node(NULL)
+{
+    GP_ASSERT(buffer);
+    AL_CHECK( alSourcei(_alSource, AL_BUFFER, buffer->_alBuffer) );
+    AL_CHECK( alSourcei(_alSource, AL_LOOPING, _looped) );
+    AL_CHECK( alSourcef(_alSource, AL_PITCH, _pitch) );
+    AL_CHECK( alSourcef(_alSource, AL_GAIN, _gain) );
+    AL_CHECK( alSourcefv(_alSource, AL_VELOCITY, (const ALfloat*)&_velocity) );
+}
+
+AudioSource::~AudioSource()
+{
+    if (_alSource)
+    {
+        AL_CHECK( alDeleteSources(1, &_alSource) );
+        _alSource = 0;
+    }
+    SAFE_RELEASE(_buffer);
+}
+
+AudioSource* AudioSource::create(const char* url)
+{
+    // Load from a .audio file.
+    std::string pathStr = url;
+    if (pathStr.find(".audio") != std::string::npos)
+    {
+        Properties* properties = Properties::create(url);
+        if (properties == NULL)
+        {
+            GP_ERROR("Failed to create audio source from .audio file.");
+            return NULL;
+        }
+
+        AudioSource* audioSource = create((strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace());
+        SAFE_DELETE(properties);
+        return audioSource;
+    }
+
+    // Create an audio buffer from this URL.
+    AudioBuffer* buffer = AudioBuffer::create(url);
+    if (buffer == NULL)
+        return NULL;
+
+    // Load the audio source.
+    ALuint alSource = 0;
+
+    AL_CHECK( alGenSources(1, &alSource) );
+    if (AL_LAST_ERROR())
+    {
+        SAFE_RELEASE(buffer);
+        GP_ERROR("Error generating audio source.");
+        return NULL;
+    }
+    
+    return new AudioSource(buffer, alSource);
+}
+
+AudioSource* AudioSource::create(Properties* properties)
+{
+    // Check if the properties is valid and has a valid namespace.
+    GP_ASSERT(properties);
+    if (!properties || !(strcmp(properties->getNamespace(), "audio") == 0))
+    {
+        GP_ERROR("Failed to load audio source from properties object: must be non-null object and have namespace equal to 'audio'.");
+        return 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.c_str());
+    if (audio == NULL)
+    {
+        GP_ERROR("Audio file '%s' failed to load properly.", path.c_str());
+        return NULL;
+    }
+
+    // Set any properties that the user specified in the .audio file.
+    if (properties->exists("looped"))
+    {
+        audio->setLooped(properties->getBool("looped"));
+    }
+    if (properties->exists("gain"))
+    {
+        audio->setGain(properties->getFloat("gain"));
+    }
+    if (properties->exists("pitch"))
+    {
+        audio->setPitch(properties->getFloat("pitch"));
+    }
+    Vector3 v;
+    if (properties->getVector3("velocity", &v))
+    {
+        audio->setVelocity(v);
+    }
+
+    return audio;
+}
+
+AudioSource::State AudioSource::getState() const
+{
+    ALint state;
+    AL_CHECK( alGetSourcei(_alSource, AL_SOURCE_STATE, &state) );
+
+    switch (state)
+    {
+        case AL_PLAYING: 
+            return PLAYING;
+        case AL_PAUSED:  
+            return PAUSED;
+        case AL_STOPPED: 
+            return STOPPED;
+        default:         
+            return INITIAL;
+    }
+    return INITIAL;
+}
+
+void AudioSource::play()
+{
+    AL_CHECK( alSourcePlay(_alSource) );
+
+    // Add the source to the controller's list of currently playing sources.
+    AudioController* audioController = Game::getInstance()->getAudioController();
+    GP_ASSERT(audioController);
+    if (audioController->_playingSources.find(this) == audioController->_playingSources.end())
+        audioController->_playingSources.insert(this);
+}
+
+void AudioSource::pause()
+{
+    AL_CHECK( alSourcePause(_alSource) );
+
+    // Remove the source from the controller's set of currently playing sources
+    // if the source is being paused by the user and not the controller itself.
+    AudioController* audioController = Game::getInstance()->getAudioController();
+    GP_ASSERT(audioController);
+    if (audioController->_pausingSource != this)
+    {
+        std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
+        if (iter != audioController->_playingSources.end())
+            audioController->_playingSources.erase(iter);
+    }
+}
+
+void AudioSource::resume()
+{
+    if (getState() == PAUSED)
+    {
+        play();
+    }
+}
+
+void AudioSource::stop()
+{
+    AL_CHECK( alSourceStop(_alSource) );
+
+    // Remove the source from the controller's set of currently playing sources.
+    AudioController* audioController = Game::getInstance()->getAudioController();
+    GP_ASSERT(audioController);
+    std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
+    if (iter != audioController->_playingSources.end())
+        audioController->_playingSources.erase(iter);
+}
+
+void AudioSource::rewind()
+{
+    AL_CHECK( alSourceRewind(_alSource) );
+}
+
+bool AudioSource::isLooped() const
+{
+    return _looped;
+}
+
+void AudioSource::setLooped(bool looped)
+{
+    AL_CHECK( alSourcei(_alSource, AL_LOOPING, (looped) ? AL_TRUE : AL_FALSE) );
+    if (AL_LAST_ERROR())
+    {
+        GP_ERROR("Failed to set audio source's looped attribute with error: %d", AL_LAST_ERROR());
+    }
+    _looped = looped;
+}
+
+float AudioSource::getGain() const
+{
+    return _gain;
+}
+
+void AudioSource::setGain(float gain)
+{
+    AL_CHECK( alSourcef(_alSource, AL_GAIN, gain) );
+    _gain = gain;
+}
+
+float AudioSource::getPitch() const
+{
+    return _pitch;
+}
+
+void AudioSource::setPitch(float pitch)
+{
+    AL_CHECK( alSourcef(_alSource, AL_PITCH, pitch) );
+    _pitch = pitch;
+}
+
+const Vector3& AudioSource::getVelocity() const
+{
+    return _velocity;
+}
+
+void AudioSource::setVelocity(const Vector3& velocity)
+{
+    AL_CHECK( alSourcefv(_alSource, AL_VELOCITY, (ALfloat*)&velocity) );
+    _velocity = velocity;
+}
+
+void AudioSource::setVelocity(float x, float y, float z)
+{
+    setVelocity(Vector3(x, y, z));
+}
+
+Node* AudioSource::getNode() const
+{
+    return _node;
+}
+
+void AudioSource::setNode(Node* node)
+{
+    if (_node != node)
+    {
+        // Disconnect our current transform.
+        if (_node)
+        {
+            _node->removeListener(this);
+        }
+
+        // Connect the new node.
+        _node = node;
+
+        if (_node)
+        {
+            _node->addListener(this);
+            // Update the audio source position.
+            transformChanged(_node, 0);
+        }
+    }
+}
+
+void AudioSource::transformChanged(Transform* transform, long cookie)
+{
+    if (_node)
+    {
+        Vector3 translation = _node->getTranslationWorld();
+        AL_CHECK( alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&translation.x) );
+    }
+}
+
+AudioSource* AudioSource::clone(NodeCloneContext &context) const
+{
+    GP_ASSERT(_buffer);
+
+    ALuint alSource = 0;
+    AL_CHECK( alGenSources(1, &alSource) );
+    if (AL_LAST_ERROR())
+    {
+        GP_ERROR("Error generating audio source.");
+        return NULL;
+    }
+    AudioSource* audioClone = new AudioSource(_buffer, alSource);
+
+    _buffer->addRef();
+    audioClone->setLooped(isLooped());
+    audioClone->setGain(getGain());
+    audioClone->setPitch(getPitch());
+    audioClone->setVelocity(getVelocity());
+    if (Node* node = getNode())
+    {
+        Node* clonedNode = context.findClonedNode(node);
+        if (clonedNode)
+        {
+            audioClone->setNode(clonedNode);
+        }
+    }
+    return audioClone;
+}
+
+}

+ 211 - 207
gameplay/src/AudioSource.h

@@ -1,207 +1,211 @@
-#ifndef AUDIOSOURCE_H_
-#define AUDIOSOURCE_H_
-
-#include "Vector3.h"
-#include "Ref.h"
-#include "Node.h"
-
-namespace gameplay
-{
-
-class AudioBuffer;
-class Node;
-class NodeCloneContext;
-
-/**
- * Declares an audio source in 3D space.
- */
-class AudioSource : public Ref, public Transform::Listener
-{
-public:
-
-    friend class Node;
-    friend class AudioController;
-
-    /**
-     * The audio source's audio state.
-     */
-    enum State
-    {
-        INITIAL,
-        PLAYING,
-        PAUSED,
-        STOPPED
-    };
-
-    /**
-     * Create an audio source. This is used to instantiate an Audio Source. Currently only wav, au, and raw files are supported.
-     * Alternately, a URL specifying a Properties object that defines an audio source can be used (where the URL is of the format
-     * "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>" and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
-     * 
-     * @param url The relative location on disk of the sound file or a URL specifying a Properties object defining an audio source.
-     * @return The newly created audio source, or NULL if an audio source cannot be created.
-     * @script{create}
-     */
-    static AudioSource* create(const char* url);
-
-    /**
-     * Create an audio source from the given properties object.
-     * 
-     * @param properties The properties object defining the audio source (must have namespace equal to 'audio').
-     * @return The newly created audio source, or <code>NULL</code> if the audio source failed to load.
-     * @script{create}
-     */
-    static AudioSource* create(Properties* properties);
-
-    /**
-     * Plays the audio source.
-     */
-    void play();
-
-    /**
-     * Pauses the playing of the audio source.
-     */
-    void pause();
-
-    /**
-     * Resumes playing of the audio source.
-     */
-    void resume();
-
-    /**
-     * Stops the playing of the audio source.
-     */
-    void stop();
-
-    /**
-     * Rewinds the audio source to the beginning.
-     */
-    void rewind();
-
-    /**
-     * Gets the current state of the audio source.
-     *
-     * @return PLAYING if the source is playing, STOPPED if the source is stopped, PAUSED if the source is paused and INITIAL otherwise.
-     */
-    AudioSource::State getState() const;
-
-    /**
-     * Determines whether the audio source is looped or not.
-     *
-     * @return true if the audio source is looped, false if not.
-     */
-    bool isLooped() const;
-
-    /**
-     * Sets the state of the audio source to be looping or not.
-     *
-     * @param looped true to loop the sound, false to not loop it.
-     */
-    void setLooped(bool looped);
-
-    /**
-     * Returns the gain of the audio source.
-     *
-     * @return The gain.
-     */
-    float getGain() const;
-
-    /**
-     * Sets the gain/volume of the audio source.
-     *
-     * @param gain The gain/volume of the source.
-     */
-    void setGain(float gain);
-
-    /**
-     * Returns the pitch of the audio source.
-     *
-     * @return The pitch.
-     */
-    float getPitch() const;
-
-    /**
-     * Sets the pitch of the audio source.
-     *
-     * @param pitch The pitch of the source.
-     */
-    void setPitch(float pitch);
-
-    /**
-     * Gets the velocity of the audio source.
-     *
-     * @return The velocity as a vector.
-     */
-    const Vector3& getVelocity() const;
-
-    /**
-     * Sets the velocity of the audio source.
-     *
-     * @param velocity A vector representing the velocity.
-     */
-    void setVelocity(const Vector3& velocity);
-
-    /**
-     * Sets the velocity of the audio source.
-     *
-     * @param x The x coordinate of the velocity.
-     * @param y The y coordinate of the velocity.
-     * @param z The z coordinate of the velocity.
-     */
-    void setVelocity(float x, float y, float z);
-
-    /**
-     * Gets the node that this source is attached to.
-     * 
-     * @return The node that this audio source is attached to.
-     */
-    Node* getNode() const;
-
-private:
-
-    /**
-     * Constructor that takes an AudioBuffer.
-     */
-    AudioSource(AudioBuffer* buffer, ALuint source);
-
-    /**
-     * Destructor.
-     */
-    virtual ~AudioSource();
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    AudioSource& operator=(const AudioSource&);
-
-    /**
-     * Sets the node for this audio source.
-     */
-    void setNode(Node* node);
-
-    /**
-     * @see Transform::Listener::transformChanged
-     */
-    void transformChanged(Transform* transform, long cookie);
-
-    /**
-     * Clones the audio source and returns a new audio source.
-     * 
-     * @param context The clone context.
-     * 
-     * @return The newly created audio source.
-     */
-    AudioSource* clone(NodeCloneContext &context) const;
-
-    ALuint _alSource;
-    AudioBuffer* _buffer;
-    bool _looped;
-    float _gain;
-    float _pitch;
-    Vector3 _velocity;
-    Node* _node;
-};
-
-}
-
-#endif
+#ifndef AUDIOSOURCE_H_
+#define AUDIOSOURCE_H_
+
+#include "Vector3.h"
+#include "Ref.h"
+#include "Node.h"
+
+namespace gameplay
+{
+
+class AudioBuffer;
+class Node;
+class NodeCloneContext;
+
+/**
+ * Defines an audio source in 3D space.
+ *
+ * This can be attached to a Node for applying its 3D transformation.
+ *
+ * @see http://blackberry.github.io/GamePlay/docs/file-formats.html#wiki-Audio
+ */
+class AudioSource : public Ref, public Transform::Listener
+{
+public:
+
+    friend class Node;
+    friend class AudioController;
+
+    /**
+     * The audio source's audio state.
+     */
+    enum State
+    {
+        INITIAL,
+        PLAYING,
+        PAUSED,
+        STOPPED
+    };
+
+    /**
+     * Create an audio source. This is used to instantiate an Audio Source. Currently only wav, au, and raw files are supported.
+     * Alternately, a URL specifying a Properties object that defines an audio source can be used (where the URL is of the format
+     * "<file-path>.<extension>#<namespace-id>/<namespace-id>/.../<namespace-id>" and "#<namespace-id>/<namespace-id>/.../<namespace-id>" is optional).
+     * 
+     * @param url The relative location on disk of the sound file or a URL specifying a Properties object defining an audio source.
+     * @return The newly created audio source, or NULL if an audio source cannot be created.
+     * @script{create}
+     */
+    static AudioSource* create(const char* url);
+
+    /**
+     * Create an audio source from the given properties object.
+     * 
+     * @param properties The properties object defining the audio source (must have namespace equal to 'audio').
+     * @return The newly created audio source, or <code>NULL</code> if the audio source failed to load.
+     * @script{create}
+     */
+    static AudioSource* create(Properties* properties);
+
+    /**
+     * Plays the audio source.
+     */
+    void play();
+
+    /**
+     * Pauses the playing of the audio source.
+     */
+    void pause();
+
+    /**
+     * Resumes playing of the audio source.
+     */
+    void resume();
+
+    /**
+     * Stops the playing of the audio source.
+     */
+    void stop();
+
+    /**
+     * Rewinds the audio source to the beginning.
+     */
+    void rewind();
+
+    /**
+     * Gets the current state of the audio source.
+     *
+     * @return PLAYING if the source is playing, STOPPED if the source is stopped, PAUSED if the source is paused and INITIAL otherwise.
+     */
+    AudioSource::State getState() const;
+
+    /**
+     * Determines whether the audio source is looped or not.
+     *
+     * @return true if the audio source is looped, false if not.
+     */
+    bool isLooped() const;
+
+    /**
+     * Sets the state of the audio source to be looping or not.
+     *
+     * @param looped true to loop the sound, false to not loop it.
+     */
+    void setLooped(bool looped);
+
+    /**
+     * Returns the gain of the audio source.
+     *
+     * @return The gain.
+     */
+    float getGain() const;
+
+    /**
+     * Sets the gain/volume of the audio source.
+     *
+     * @param gain The gain/volume of the source.
+     */
+    void setGain(float gain);
+
+    /**
+     * Returns the pitch of the audio source.
+     *
+     * @return The pitch.
+     */
+    float getPitch() const;
+
+    /**
+     * Sets the pitch of the audio source.
+     *
+     * @param pitch The pitch of the source.
+     */
+    void setPitch(float pitch);
+
+    /**
+     * Gets the velocity of the audio source.
+     *
+     * @return The velocity as a vector.
+     */
+    const Vector3& getVelocity() const;
+
+    /**
+     * Sets the velocity of the audio source.
+     *
+     * @param velocity A vector representing the velocity.
+     */
+    void setVelocity(const Vector3& velocity);
+
+    /**
+     * Sets the velocity of the audio source.
+     *
+     * @param x The x coordinate of the velocity.
+     * @param y The y coordinate of the velocity.
+     * @param z The z coordinate of the velocity.
+     */
+    void setVelocity(float x, float y, float z);
+
+    /**
+     * Gets the node that this source is attached to.
+     * 
+     * @return The node that this audio source is attached to.
+     */
+    Node* getNode() const;
+
+private:
+
+    /**
+     * Constructor that takes an AudioBuffer.
+     */
+    AudioSource(AudioBuffer* buffer, ALuint source);
+
+    /**
+     * Destructor.
+     */
+    virtual ~AudioSource();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    AudioSource& operator=(const AudioSource&);
+
+    /**
+     * Sets the node for this audio source.
+     */
+    void setNode(Node* node);
+
+    /**
+     * @see Transform::Listener::transformChanged
+     */
+    void transformChanged(Transform* transform, long cookie);
+
+    /**
+     * Clones the audio source and returns a new audio source.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created audio source.
+     */
+    AudioSource* clone(NodeCloneContext &context) const;
+
+    ALuint _alSource;
+    AudioBuffer* _buffer;
+    bool _looped;
+    float _gain;
+    float _pitch;
+    Vector3 _velocity;
+    Node* _node;
+};
+
+}
+
+#endif

+ 358 - 352
gameplay/src/Base.h

@@ -1,352 +1,358 @@
-#ifndef BASE_H_
-#define BASE_H_
-
-// C/C++
-#include <new>
-#include <memory>
-#include <cstdio>
-#include <cstdlib>
-#include <cassert>
-#include <cwchar>
-#include <cwctype>
-#include <cctype>
-#include <cmath>
-#include <cstdarg>
-#include <ctime>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <cstring>
-#include <vector>
-#include <list>
-#include <set>
-#include <stack>
-#include <map>
-#include <queue>
-#include <algorithm>
-#include <limits>
-#include <functional>
-#include <bitset>
-#include <typeinfo>
-#include "Logger.h"
-
-// Bring common functions from C into global namespace
-using std::memcpy;
-using std::fabs;
-using std::sqrt;
-using std::cos;
-using std::sin;
-using std::tan;
-using std::isspace;
-using std::isdigit;
-using std::toupper;
-using std::tolower;
-using std::size_t;
-using std::min;
-using std::max;
-using std::modf;
-
-// Common
-#ifndef NULL
-#define NULL     0
-#endif
-
-namespace gameplay
-{
-/**
- * Print logging (implemented per platform).
- * @script{ignore}
- */
-extern void print(const char* format, ...);
-}
-
-// Current function macro.
-#ifdef WIN32
-#define __current__func__ __FUNCTION__
-#else
-#define __current__func__ __func__
-#endif
-
-// Assert macros.
-#ifdef _DEBUG
-#define GP_ASSERT(expression) assert(expression)
-#else
-#define GP_ASSERT(expression)
-#endif
-
-// Error macro.
-#ifdef GP_ERRORS_AS_WARNINGS
-#define GP_ERROR GP_WARN
-#else
-#define GP_ERROR(...) do \
-    { \
-        gameplay::Logger::log(gameplay::Logger::LEVEL_ERROR, "%s -- ", __current__func__); \
-        gameplay::Logger::log(gameplay::Logger::LEVEL_ERROR, __VA_ARGS__); \
-        gameplay::Logger::log(gameplay::Logger::LEVEL_ERROR, "\n"); \
-        assert(0); \
-        std::exit(-1); \
-    } while (0)
-#endif
-
-// Warning macro.
-#define GP_WARN(...) do \
-    { \
-        gameplay::Logger::log(gameplay::Logger::LEVEL_WARN, "%s -- ", __current__func__); \
-        gameplay::Logger::log(gameplay::Logger::LEVEL_WARN, __VA_ARGS__); \
-        gameplay::Logger::log(gameplay::Logger::LEVEL_WARN, "\n"); \
-    } while (0)
-
-// Bullet Physics
-#include <btBulletDynamicsCommon.h>
-#include <BulletCollision/CollisionDispatch/btGhostObject.h>
-#define BV(v) (btVector3((v).x, (v).y, (v).z))
-#define BQ(q) (btQuaternion((q).x, (q).y, (q).z, (q).w))
-
-// Debug new for memory leak detection
-#include "DebugNew.h"
-
-// Object deletion macro
-#define SAFE_DELETE(x) \
-    { \
-        delete x; \
-        x = NULL; \
-    }
-
-// Array deletion macro
-#define SAFE_DELETE_ARRAY(x) \
-    { \
-        delete[] x; \
-        x = NULL; \
-    }
-
-// Ref cleanup macro
-#define SAFE_RELEASE(x) \
-    if (x) \
-    { \
-        (x)->release(); \
-        x = NULL; \
-    }
-
-// Math
-#define MATH_DEG_TO_RAD(x)          ((x) * 0.0174532925f)
-#define MATH_RAD_TO_DEG(x)          ((x)* 57.29577951f)
-#define MATH_RANDOM_MINUS1_1()      ((2.0f*((float)rand()/RAND_MAX))-1.0f)      // Returns a random float between -1 and 1.
-#define MATH_RANDOM_0_1()           ((float)rand()/RAND_MAX)                    // Returns a random float between 0 and 1.
-#define MATH_FLOAT_SMALL            1.0e-37f
-#define MATH_TOLERANCE              2e-37f
-#define MATH_E                      2.71828182845904523536f
-#define MATH_LOG10E                 0.4342944819032518f
-#define MATH_LOG2E                  1.442695040888963387f
-#define MATH_PI                     3.14159265358979323846f
-#define MATH_PIOVER2                1.57079632679489661923f
-#define MATH_PIOVER4                0.785398163397448309616f
-#define MATH_PIX2                   6.28318530717958647693f
-#define MATH_EPSILON                0.000001f
-#define MATH_CLAMP(x, lo, hi)       ((x < lo) ? lo : ((x > hi) ? hi : x))
-#ifndef M_1_PI
-#define M_1_PI                      0.31830988618379067154
-#endif
-
-#ifdef WIN32
-    inline float round(float r)
-    {
-        return (r > 0.0f) ? floor(r + 0.5f) : ceil(r - 0.5f);
-    }
-#endif
-
-// NOMINMAX makes sure that windef.h doesn't add macros min and max
-#ifdef WIN32
-    #define NOMINMAX
-#endif
-
-// Audio (OpenAL/Vorbis)
-#ifdef __QNX__
-#include <AL/al.h>
-#include <AL/alc.h>
-#elif __ANDROID__
-#include <AL/al.h>
-#include <AL/alc.h>
-#elif __linux__
-#include <AL/al.h>
-#include <AL/alc.h>
-#elif WIN32
-#include <al.h>
-#include <alc.h>
-#elif __APPLE__
-#include <OpenAL/al.h>
-#include <OpenAL/alc.h>
-#endif
-#include <vorbis/vorbisfile.h>
-
-// Image
-#include <png.h>
-
-// Scripting
-using std::va_list;
-#include <lua.hpp>
-
-#define WINDOW_VSYNC        1
-
-// Graphics (OpenGL)
-#ifdef __QNX__
-    #include <EGL/egl.h>
-    #include <GLES2/gl2.h>
-    #include <GLES2/gl2ext.h>
-    #include <screen/screen.h>
-    extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray;
-    extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays;
-    extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays;
-    extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
-    #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
-    #define glClearDepth glClearDepthf
-    #define OPENGL_ES
-    #define USE_PVRTC
-    #ifdef __arm__
-        #define USE_NEON
-    #endif
-#elif __ANDROID__
-    #include <EGL/egl.h>
-    #include <GLES2/gl2.h>
-    #include <GLES2/gl2ext.h>
-    extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray;
-    extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays;
-    extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays;
-    extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
-    #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
-    #define glClearDepth glClearDepthf
-    #define OPENGL_ES
-#elif WIN32
-    #define WIN32_LEAN_AND_MEAN
-    #define GLEW_STATIC
-    #include <GL/glew.h>
-    #define USE_VAO
-#elif __linux__
-        #define GLEW_STATIC
-        #include <GL/glew.h>
-        #define USE_VAO
-#elif __APPLE__
-    #include "TargetConditionals.h"
-    #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
-        #include <OpenGLES/ES2/gl.h>
-        #include <OpenGLES/ES2/glext.h>
-        #define glBindVertexArray glBindVertexArrayOES
-        #define glDeleteVertexArrays glDeleteVertexArraysOES
-        #define glGenVertexArrays glGenVertexArraysOES
-        #define glIsVertexArray glIsVertexArrayOES
-        #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
-        #define glClearDepth glClearDepthf
-        #define OPENGL_ES
-        #define USE_VAO
-        #ifdef __arm__
-            #define USE_NEON
-        #endif
-    #elif TARGET_OS_MAC
-        #include <OpenGL/gl.h>
-        #include <OpenGL/glext.h>
-        #define glBindVertexArray glBindVertexArrayAPPLE
-        #define glDeleteVertexArrays glDeleteVertexArraysAPPLE
-        #define glGenVertexArrays glGenVertexArraysAPPLE
-        #define glIsVertexArray glIsVertexArrayAPPLE
-        #define USE_VAO
-    #else
-        #error "Unsupported Apple Device"
-    #endif
-#endif
-
-// Graphics (GLSL)
-#define VERTEX_ATTRIBUTE_POSITION_NAME              "a_position"
-#define VERTEX_ATTRIBUTE_NORMAL_NAME                "a_normal"
-#define VERTEX_ATTRIBUTE_COLOR_NAME                 "a_color"
-#define VERTEX_ATTRIBUTE_TANGENT_NAME               "a_tangent"
-#define VERTEX_ATTRIBUTE_BINORMAL_NAME              "a_binormal"
-#define VERTEX_ATTRIBUTE_BLENDWEIGHTS_NAME          "a_blendWeights"
-#define VERTEX_ATTRIBUTE_BLENDINDICES_NAME          "a_blendIndices"
-#define VERTEX_ATTRIBUTE_TEXCOORD_PREFIX_NAME       "a_texCoord"
-
-// Hardware buffer
-namespace gameplay
-{
-/** Vertex attribute. */
-typedef GLint VertexAttribute;
-/** Vertex buffer handle. */
-typedef GLuint VertexBufferHandle;
-/** Index buffer handle. */
-typedef GLuint IndexBufferHandle;
-/** Texture handle. */
-typedef GLuint TextureHandle;
-/** Frame buffer handle. */
-typedef GLuint FrameBufferHandle;
-/** Render buffer handle. */
-typedef GLuint RenderBufferHandle;
-
-/** Gamepad handle definitions vary by platform. */
-#if defined(__QNX__) && defined(USE_BLACKBERRY_GAMEPAD)
-    typedef screen_device_t GamepadHandle;
-#elif defined(USE_XINPUT)
-    typedef unsigned long GamepadHandle;
-#else
-    typedef unsigned int GamepadHandle;
-#endif
-}
-
-/**
- * GL assertion that can be used for any OpenGL function call.
- *
- * This macro will assert if an error is detected when executing
- * the specified GL code. This macro will do nothing in release
- * mode and is therefore safe to use for realtime/per-frame GL
- * function calls.
- */
-#ifdef NDEBUG
-#define GL_ASSERT( gl_code ) gl_code
-#else
-#define GL_ASSERT( gl_code ) do \
-    { \
-        gl_code; \
-        __gl_error_code = glGetError(); \
-        GP_ASSERT(__gl_error_code == GL_NO_ERROR); \
-    } while(0)
-#endif
-
-/** Global variable to hold GL errors
- * @script{ignore} */
-extern GLenum __gl_error_code;
-
-/**
- * Executes the specified AL code and checks the AL error afterwards
- * to ensure it succeeded.
- *
- * The AL_LAST_ERROR macro can be used afterwards to check whether a AL error was
- * encountered executing the specified code.
- */
-#define AL_CHECK( al_code ) do \
-    { \
-        while (alGetError() != AL_NO_ERROR) ; \
-        al_code; \
-        __al_error_code = alGetError(); \
-        if (__al_error_code != AL_NO_ERROR) \
-        { \
-            GP_ERROR(#al_code ": %d", (int)__al_error_code); \
-        } \
-    } while(0)
-
-/** Global variable to hold AL errors
- * @script{ignore} */
-extern ALenum __al_error_code;
-
-/**
- * Accesses the most recently set global AL error.
- */
-#define AL_LAST_ERROR() __al_error_code
-
-
-#if defined(WIN32)
-    #pragma warning( disable : 4172 )
-    #pragma warning( disable : 4244 )
-    #pragma warning( disable : 4311 )
-    #pragma warning( disable : 4390 )
-    #pragma warning( disable : 4800 )
-    #pragma warning( disable : 4996 )
-#endif
-
-#endif
+#ifndef BASE_H_
+#define BASE_H_
+
+// C/C++
+#include <new>
+#include <memory>
+#include <cstdio>
+#include <cstdlib>
+#include <cassert>
+#include <cwchar>
+#include <cwctype>
+#include <cctype>
+#include <cmath>
+#include <cstdarg>
+#include <ctime>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <cstring>
+#include <vector>
+#include <list>
+#include <set>
+#include <stack>
+#include <map>
+#include <queue>
+#include <algorithm>
+#include <limits>
+#include <functional>
+#include <bitset>
+#include <typeinfo>
+#include "Logger.h"
+
+// Bring common functions from C into global namespace
+using std::memcpy;
+using std::fabs;
+using std::sqrt;
+using std::cos;
+using std::sin;
+using std::tan;
+using std::isspace;
+using std::isdigit;
+using std::toupper;
+using std::tolower;
+using std::size_t;
+using std::min;
+using std::max;
+using std::modf;
+using std::atoi;
+
+// Common
+#ifndef NULL
+#define NULL     0
+#endif
+
+namespace gameplay
+{
+/**
+ * Print logging (implemented per platform).
+ * @script{ignore}
+ */
+extern void print(const char* format, ...);
+
+// Define a platform-independent case-insensitive ASCII string comparison function.
+extern int strcmpnocase(const char* s1, const char* s2);
+}
+
+// Current function macro.
+#ifdef WIN32
+#define __current__func__ __FUNCTION__
+#else
+#define __current__func__ __func__
+#endif
+
+// Assert macros.
+#ifdef _DEBUG
+#define GP_ASSERT(expression) assert(expression)
+#else
+#define GP_ASSERT(expression)
+#endif
+
+#if defined(WIN32) && defined(_MSC_VER)
+#define DEBUG_BREAK() __debugbreak()
+#else
+#define DEBUG_BREAK()
+#endif
+
+// Error macro.
+#ifdef GP_ERRORS_AS_WARNINGS
+#define GP_ERROR GP_WARN
+#else
+#define GP_ERROR(...) do \
+    { \
+        gameplay::Logger::log(gameplay::Logger::LEVEL_ERROR, "%s -- ", __current__func__); \
+        gameplay::Logger::log(gameplay::Logger::LEVEL_ERROR, __VA_ARGS__); \
+        gameplay::Logger::log(gameplay::Logger::LEVEL_ERROR, "\n"); \
+        DEBUG_BREAK(); \
+        assert(0); \
+        std::exit(-1); \
+    } while (0)
+#endif
+
+// Warning macro.
+#define GP_WARN(...) do \
+    { \
+        gameplay::Logger::log(gameplay::Logger::LEVEL_WARN, "%s -- ", __current__func__); \
+        gameplay::Logger::log(gameplay::Logger::LEVEL_WARN, __VA_ARGS__); \
+        gameplay::Logger::log(gameplay::Logger::LEVEL_WARN, "\n"); \
+    } while (0)
+
+#if defined(WIN32)
+    #pragma warning( disable : 4005 )
+    #pragma warning( disable : 4172 )
+    #pragma warning( disable : 4244 )
+    #pragma warning( disable : 4267 )
+    #pragma warning( disable : 4311 )
+	#pragma warning( disable : 4316 )
+    #pragma warning( disable : 4390 )
+    #pragma warning( disable : 4800 )
+    #pragma warning( disable : 4996 )
+#endif
+
+// Bullet Physics
+#include <btBulletDynamicsCommon.h>
+#include <BulletCollision/CollisionDispatch/btGhostObject.h>
+#define BV(v) (btVector3((v).x, (v).y, (v).z))
+#define BQ(q) (btQuaternion((q).x, (q).y, (q).z, (q).w))
+
+// Debug new for memory leak detection
+#include "DebugNew.h"
+
+// Object deletion macro
+#define SAFE_DELETE(x) \
+    { \
+        delete x; \
+        x = NULL; \
+    }
+
+// Array deletion macro
+#define SAFE_DELETE_ARRAY(x) \
+    { \
+        delete[] x; \
+        x = NULL; \
+    }
+
+// Ref cleanup macro
+#define SAFE_RELEASE(x) \
+    if (x) \
+    { \
+        (x)->release(); \
+        x = NULL; \
+    }
+
+// Math
+#define MATH_DEG_TO_RAD(x)          ((x) * 0.0174532925f)
+#define MATH_RAD_TO_DEG(x)          ((x)* 57.29577951f)
+#define MATH_RANDOM_MINUS1_1()      ((2.0f*((float)rand()/RAND_MAX))-1.0f)      // Returns a random float between -1 and 1.
+#define MATH_RANDOM_0_1()           ((float)rand()/RAND_MAX)                    // Returns a random float between 0 and 1.
+#define MATH_FLOAT_SMALL            1.0e-37f
+#define MATH_TOLERANCE              2e-37f
+#define MATH_E                      2.71828182845904523536f
+#define MATH_LOG10E                 0.4342944819032518f
+#define MATH_LOG2E                  1.442695040888963387f
+#define MATH_PI                     3.14159265358979323846f
+#define MATH_PIOVER2                1.57079632679489661923f
+#define MATH_PIOVER4                0.785398163397448309616f
+#define MATH_PIX2                   6.28318530717958647693f
+#define MATH_EPSILON                0.000001f
+#define MATH_CLAMP(x, lo, hi)       ((x < lo) ? lo : ((x > hi) ? hi : x))
+#ifndef M_1_PI
+#define M_1_PI                      0.31830988618379067154
+#endif
+
+// NOMINMAX makes sure that windef.h doesn't add macros min and max
+#ifdef WIN32
+    #define NOMINMAX
+#endif
+
+// Audio (OpenAL/Vorbis)
+#ifdef __QNX__
+#include <AL/al.h>
+#include <AL/alc.h>
+#elif __ANDROID__
+#include <AL/al.h>
+#include <AL/alc.h>
+#elif __linux__
+#include <AL/al.h>
+#include <AL/alc.h>
+#elif WIN32
+#include <al.h>
+#include <alc.h>
+#elif __APPLE__
+#include <OpenAL/al.h>
+#include <OpenAL/alc.h>
+#endif
+#include <vorbis/vorbisfile.h>
+
+// Image
+#include <png.h>
+
+// Scripting
+using std::va_list;
+#include <lua.hpp>
+
+#define WINDOW_VSYNC        1
+
+// Graphics (OpenGL)
+#ifdef __QNX__
+    #include <EGL/egl.h>
+    #include <GLES2/gl2.h>
+    #include <GLES2/gl2ext.h>
+    #include <screen/screen.h>
+    extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray;
+    extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays;
+    extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays;
+    extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
+    #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
+    #define glClearDepth glClearDepthf
+    #define OPENGL_ES
+    #define USE_PVRTC
+    #ifdef __arm__
+        #define USE_NEON
+    #endif
+#elif __ANDROID__
+    #include <EGL/egl.h>
+    #include <GLES2/gl2.h>
+    #include <GLES2/gl2ext.h>
+    extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray;
+    extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays;
+    extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays;
+    extern PFNGLISVERTEXARRAYOESPROC glIsVertexArray;
+    #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
+    #define glClearDepth glClearDepthf
+    #define OPENGL_ES
+#elif WIN32
+    #define WIN32_LEAN_AND_MEAN
+    #define GLEW_STATIC
+    #include <GL/glew.h>
+    #define USE_VAO
+#elif __linux__
+        #define GLEW_STATIC
+        #include <GL/glew.h>
+        #define USE_VAO
+#elif __APPLE__
+    #include "TargetConditionals.h"
+    #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+        #include <OpenGLES/ES2/gl.h>
+        #include <OpenGLES/ES2/glext.h>
+        #define glBindVertexArray glBindVertexArrayOES
+        #define glDeleteVertexArrays glDeleteVertexArraysOES
+        #define glGenVertexArrays glGenVertexArraysOES
+        #define glIsVertexArray glIsVertexArrayOES
+        #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
+        #define glClearDepth glClearDepthf
+        #define OPENGL_ES
+        #define USE_VAO
+        #ifdef __arm__
+            #define USE_NEON
+        #endif
+    #elif TARGET_OS_MAC
+        #include <OpenGL/gl.h>
+        #include <OpenGL/glext.h>
+        #define glBindVertexArray glBindVertexArrayAPPLE
+        #define glDeleteVertexArrays glDeleteVertexArraysAPPLE
+        #define glGenVertexArrays glGenVertexArraysAPPLE
+        #define glIsVertexArray glIsVertexArrayAPPLE
+        #define USE_VAO
+    #else
+        #error "Unsupported Apple Device"
+    #endif
+#endif
+
+// Graphics (GLSL)
+#define VERTEX_ATTRIBUTE_POSITION_NAME              "a_position"
+#define VERTEX_ATTRIBUTE_NORMAL_NAME                "a_normal"
+#define VERTEX_ATTRIBUTE_COLOR_NAME                 "a_color"
+#define VERTEX_ATTRIBUTE_TANGENT_NAME               "a_tangent"
+#define VERTEX_ATTRIBUTE_BINORMAL_NAME              "a_binormal"
+#define VERTEX_ATTRIBUTE_BLENDWEIGHTS_NAME          "a_blendWeights"
+#define VERTEX_ATTRIBUTE_BLENDINDICES_NAME          "a_blendIndices"
+#define VERTEX_ATTRIBUTE_TEXCOORD_PREFIX_NAME       "a_texCoord"
+
+// Hardware buffer
+namespace gameplay
+{
+/** Vertex attribute. */
+typedef GLint VertexAttribute;
+/** Vertex buffer handle. */
+typedef GLuint VertexBufferHandle;
+/** Index buffer handle. */
+typedef GLuint IndexBufferHandle;
+/** Texture handle. */
+typedef GLuint TextureHandle;
+/** Frame buffer handle. */
+typedef GLuint FrameBufferHandle;
+/** Render buffer handle. */
+typedef GLuint RenderBufferHandle;
+
+/** Gamepad handle definitions vary by platform. */
+#if defined(__QNX__) && defined(GP_USE_GAMEPAD)
+    typedef screen_device_t GamepadHandle;
+#elif defined(WIN32)
+    typedef unsigned long GamepadHandle;
+#else
+    typedef unsigned int GamepadHandle;
+#endif
+}
+
+/**
+ * GL assertion that can be used for any OpenGL function call.
+ *
+ * This macro will assert if an error is detected when executing
+ * the specified GL code. This macro will do nothing in release
+ * mode and is therefore safe to use for realtime/per-frame GL
+ * function calls.
+ */
+#ifdef NDEBUG
+#define GL_ASSERT( gl_code ) gl_code
+#else
+#define GL_ASSERT( gl_code ) do \
+    { \
+        gl_code; \
+        __gl_error_code = glGetError(); \
+        GP_ASSERT(__gl_error_code == GL_NO_ERROR); \
+    } while(0)
+#endif
+
+/** Global variable to hold GL errors
+ * @script{ignore} */
+extern GLenum __gl_error_code;
+
+/**
+ * Executes the specified AL code and checks the AL error afterwards
+ * to ensure it succeeded.
+ *
+ * The AL_LAST_ERROR macro can be used afterwards to check whether a AL error was
+ * encountered executing the specified code.
+ */
+#define AL_CHECK( al_code ) do \
+    { \
+        while (alGetError() != AL_NO_ERROR) ; \
+        al_code; \
+        __al_error_code = alGetError(); \
+        if (__al_error_code != AL_NO_ERROR) \
+        { \
+            GP_ERROR(#al_code ": %d", (int)__al_error_code); \
+        } \
+    } while(0)
+
+/** Global variable to hold AL errors
+ * @script{ignore} */
+extern ALenum __al_error_code;
+
+/**
+ * Accesses the most recently set global AL error.
+ */
+#define AL_LAST_ERROR() __al_error_code
+
+#endif

+ 349 - 349
gameplay/src/BoundingBox.cpp

@@ -1,349 +1,349 @@
-#include "Base.h"
-#include "BoundingBox.h"
-#include "BoundingSphere.h"
-#include "Plane.h"
-
-namespace gameplay
-{
-
-BoundingBox::BoundingBox()
-{
-}
-
-BoundingBox::BoundingBox(const Vector3& min, const Vector3& max)
-{
-    set(min, max);
-}
-
-BoundingBox::BoundingBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
-{
-    set(minX, minY, minZ, maxX, maxY, maxZ);
-}
-
-BoundingBox::BoundingBox(const BoundingBox& copy)
-{
-    set(copy);
-}
-
-BoundingBox::~BoundingBox()
-{
-}
-
-const BoundingBox& BoundingBox::empty()
-{
-    static BoundingBox b;
-    return b;
-}
-
-void BoundingBox::getCorners(Vector3* dst) const
-{
-    GP_ASSERT(dst);
-
-    // Near face, specified counter-clockwise looking towards the origin from the positive z-axis.
-    // Left-top-front.
-    dst[0].set(min.x, max.y, max.z);
-    // Left-bottom-front.
-    dst[1].set(min.x, min.y, max.z);
-    // Right-bottom-front.
-    dst[2].set(max.x, min.y, max.z);
-    // Right-top-front.
-    dst[3].set(max.x, max.y, max.z);
-
-    // Far face, specified counter-clockwise looking towards the origin from the negative z-axis.
-    // Right-top-back.
-    dst[4].set(max.x, max.y, min.z);
-    // Right-bottom-back.
-    dst[5].set(max.x, min.y, min.z);
-    // Left-bottom-back.
-    dst[6].set(min.x, min.y, min.z);
-    // Left-top-back.
-    dst[7].set(min.x, max.y, min.z);
-}
-
-Vector3 BoundingBox::getCenter() const
-{
-    Vector3 center;
-    getCenter(&center);
-    return center;
-}
-
-void BoundingBox::getCenter(Vector3* dst) const
-{
-    GP_ASSERT(dst);
-
-    dst->set(min, max);
-    dst->scale(0.5f);
-    dst->add(min);
-}
-
-bool BoundingBox::intersects(const BoundingSphere& sphere) const
-{
-    return sphere.intersects(*this);
-}
-
-bool BoundingBox::intersects(const BoundingBox& box) const
-{
-    return ((min.x >= box.min.x && min.x <= box.max.x) || (box.min.x >= min.x && box.min.x <= max.x)) &&
-            ((min.y >= box.min.y && min.y <= box.max.y) || (box.min.y >= min.y && box.min.y <= max.y)) &&
-            ((min.z >= box.min.z && min.z <= box.max.z) || (box.min.z >= min.z && box.min.z <= max.z));
-}
-
-bool BoundingBox::intersects(const Frustum& frustum) const
-{
-    // The box must either intersect or be in the positive half-space of all six planes of the frustum.
-    return (intersects(frustum.getNear()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getFar()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getLeft()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getRight()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getBottom()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getTop()) != Plane::INTERSECTS_BACK);
-}
-
-float BoundingBox::intersects(const Plane& plane) const
-{
-    // Calculate the distance from the center of the box to the plane.
-    Vector3 center((min.x + max.x) * 0.5f, (min.y + max.y) * 0.5f, (min.z + max.z) * 0.5f);
-    float distance = plane.distance(center);
-
-    // Get the extents of the box from its center along each axis.
-    float extentX = (max.x - min.x) * 0.5f;
-    float extentY = (max.y - min.y) * 0.5f;
-    float extentZ = (max.z - min.z) * 0.5f;
-
-    const Vector3& planeNormal = plane.getNormal();
-    if (fabsf(distance) <= (fabsf(extentX * planeNormal.x) + fabsf(extentY * planeNormal.y) + fabsf(
-        extentZ * planeNormal.z)))
-    {
-        return Plane::INTERSECTS_INTERSECTING;
-    }
-
-    return (distance > 0.0f) ? (float)Plane::INTERSECTS_FRONT : (float)Plane::INTERSECTS_BACK;
-}
-
-float BoundingBox::intersects(const Ray& ray) const
-{
-    // Intermediate calculation variables.
-    float dnear = 0.0f;
-    float dfar = 0.0f;
-    float tmin = 0.0f;
-    float tmax = 0.0f;
-
-    const Vector3& origin = ray.getOrigin();
-    const Vector3& direction = ray.getDirection();
-
-    // X direction.
-    float div = 1.0f / direction.x;
-    if (div >= 0.0f)
-    {
-        tmin = (min.x - origin.x) * div;
-        tmax = (max.x - origin.x) * div;
-    }
-    else
-    {
-        tmin = (max.x - origin.x) * div;
-        tmax = (min.x - origin.x) * div;
-    }
-    dnear = tmin;
-    dfar = tmax;
-
-    // Check if the ray misses the box.
-    if (dnear > dfar || dfar < 0.0f)
-    {
-        return Ray::INTERSECTS_NONE;
-    }
-
-    // Y direction.
-    div = 1.0f / direction.y;
-    if (div >= 0.0f)
-    {
-        tmin = (min.y - origin.y) * div;
-        tmax = (max.y - origin.y) * div;
-    }
-    else
-    {
-        tmin = (max.y - origin.y) * div;
-        tmax = (min.y - origin.y) * div;
-    }
-
-    // Update the near and far intersection distances.
-    if (tmin > dnear)
-    {
-        dnear = tmin;
-    }
-    if (tmax < dfar)
-    {
-        dfar = tmax;
-    }
-    // Check if the ray misses the box.
-    if (dnear > dfar || dfar < 0.0f)
-    {
-        return Ray::INTERSECTS_NONE;
-    }
-
-    // Z direction.
-    div = 1.0f / direction.z;
-    if (div >= 0.0f)
-    {
-        tmin = (min.z - origin.z) * div;
-        tmax = (max.z - origin.z) * div;
-    }
-    else
-    {
-        tmin = (max.z - origin.z) * div;
-        tmax = (min.z - origin.z) * div;
-    }
-
-    // Update the near and far intersection distances.
-    if (tmin > dnear)
-    {
-        dnear = tmin;
-    }
-    if (tmax < dfar)
-    {
-        dfar = tmax;
-    }
-
-    // Check if the ray misses the box.
-    if (dnear > dfar || dfar < 0.0f)
-    {
-        return Ray::INTERSECTS_NONE;
-    }
-    // The ray intersects the box (and since the direction of a Ray is normalized, dnear is the distance to the ray).
-    return dnear;
-}
-
-bool BoundingBox::isEmpty() const
-{
-    return min.x == max.x && min.y == max.y && min.z == max.z;
-}
-
-void BoundingBox::merge(const BoundingBox& box)
-{
-    // Calculate the new minimum point.
-    min.x = std::min(min.x, box.min.x);
-    min.y = std::min(min.y, box.min.y);
-    min.z = std::min(min.z, box.min.z);
-
-    // Calculate the new maximum point.
-    max.x = std::max(max.x, box.max.x);
-    max.y = std::max(max.y, box.max.y);
-    max.z = std::max(max.z, box.max.z);
-}
-
-void BoundingBox::merge(const BoundingSphere& sphere)
-{
-    const Vector3& center = sphere.center;
-    float radius = sphere.radius;
-
-    // Calculate the new minimum point for the merged bounding box.
-    min.x = std::min(min.x, center.x - radius);
-    min.y = std::min(min.y, center.y - radius);
-    min.z = std::min(min.z, center.z - radius);
-
-    // Calculate the new maximum point for the merged bounding box.
-    max.x = std::max(max.x, center.x + radius);
-    max.y = std::max(max.y, center.y + radius);
-    max.z = std::max(max.z, center.z + radius);
-}
-
-void BoundingBox::set(const Vector3& min, const Vector3& max)
-{
-    this->min = min;
-    this->max = max;
-}
-
-void BoundingBox::set(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
-{
-    min.set(minX, minY, minZ);
-    max.set(maxX, maxY, maxZ);
-}
-
-static void updateMinMax(Vector3* point, Vector3* min, Vector3* max)
-{
-    GP_ASSERT(point);
-    GP_ASSERT(min);
-    GP_ASSERT(max);
-
-    // Leftmost point.
-    if (point->x < min->x)
-    {
-        min->x = point->x;
-    }
-
-    // Rightmost point.
-    if (point->x > max->x)
-    {
-        max->x = point->x;
-    }
-
-    // Lowest point.
-    if (point->y < min->y)
-    {
-        min->y = point->y;
-    }
-
-    // Highest point.
-    if (point->y > max->y)
-    {
-        max->y = point->y;
-    }
-
-    // Farthest point.
-    if (point->z < min->z)
-    {
-        min->z = point->z;
-    }
-
-    // Nearest point.
-    if (point->z > max->z)
-    {
-        max->z = point->z;
-    }
-}
-
-void BoundingBox::set(const BoundingBox& box)
-{
-    min = box.min;
-    max = box.max;
-}
-
-void BoundingBox::set(const BoundingSphere& sphere)
-{
-    const Vector3& center = sphere.center;
-    float radius = sphere.radius;
-
-    // Calculate the minimum point for the box.
-    min.x = center.x - radius;
-    min.y = center.y - radius;
-    min.z = center.z - radius;
-
-    // Calculate the maximum point for the box.
-    max.x = center.x + radius;
-    max.y = center.y + radius;
-    max.z = center.z + radius;
-}
-
-void BoundingBox::transform(const Matrix& matrix)
-{
-    // Calculate the corners.
-    Vector3 corners[8];
-    getCorners(corners);
-
-    // Transform the corners, recalculating the min and max points along the way.
-    matrix.transformPoint(&corners[0]);
-    Vector3 newMin = corners[0];
-    Vector3 newMax = corners[0];
-    for (int i = 1; i < 8; i++)
-    {
-        matrix.transformPoint(&corners[i]);
-        updateMinMax(&corners[i], &newMin, &newMax);
-    }
-    this->min.x = newMin.x;
-    this->min.y = newMin.y;
-    this->min.z = newMin.z;
-    this->max.x = newMax.x;
-    this->max.y = newMax.y;
-    this->max.z = newMax.z;
-}
-
-}
+#include "Base.h"
+#include "BoundingBox.h"
+#include "BoundingSphere.h"
+#include "Plane.h"
+
+namespace gameplay
+{
+
+BoundingBox::BoundingBox()
+{
+}
+
+BoundingBox::BoundingBox(const Vector3& min, const Vector3& max)
+{
+    set(min, max);
+}
+
+BoundingBox::BoundingBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
+{
+    set(minX, minY, minZ, maxX, maxY, maxZ);
+}
+
+BoundingBox::BoundingBox(const BoundingBox& copy)
+{
+    set(copy);
+}
+
+BoundingBox::~BoundingBox()
+{
+}
+
+const BoundingBox& BoundingBox::empty()
+{
+    static BoundingBox b;
+    return b;
+}
+
+void BoundingBox::getCorners(Vector3* dst) const
+{
+    GP_ASSERT(dst);
+
+    // Near face, specified counter-clockwise looking towards the origin from the positive z-axis.
+    // Left-top-front.
+    dst[0].set(min.x, max.y, max.z);
+    // Left-bottom-front.
+    dst[1].set(min.x, min.y, max.z);
+    // Right-bottom-front.
+    dst[2].set(max.x, min.y, max.z);
+    // Right-top-front.
+    dst[3].set(max.x, max.y, max.z);
+
+    // Far face, specified counter-clockwise looking towards the origin from the negative z-axis.
+    // Right-top-back.
+    dst[4].set(max.x, max.y, min.z);
+    // Right-bottom-back.
+    dst[5].set(max.x, min.y, min.z);
+    // Left-bottom-back.
+    dst[6].set(min.x, min.y, min.z);
+    // Left-top-back.
+    dst[7].set(min.x, max.y, min.z);
+}
+
+Vector3 BoundingBox::getCenter() const
+{
+    Vector3 center;
+    getCenter(&center);
+    return center;
+}
+
+void BoundingBox::getCenter(Vector3* dst) const
+{
+    GP_ASSERT(dst);
+
+    dst->set(min, max);
+    dst->scale(0.5f);
+    dst->add(min);
+}
+
+bool BoundingBox::intersects(const BoundingSphere& sphere) const
+{
+    return sphere.intersects(*this);
+}
+
+bool BoundingBox::intersects(const BoundingBox& box) const
+{
+    return ((min.x >= box.min.x && min.x <= box.max.x) || (box.min.x >= min.x && box.min.x <= max.x)) &&
+            ((min.y >= box.min.y && min.y <= box.max.y) || (box.min.y >= min.y && box.min.y <= max.y)) &&
+            ((min.z >= box.min.z && min.z <= box.max.z) || (box.min.z >= min.z && box.min.z <= max.z));
+}
+
+bool BoundingBox::intersects(const Frustum& frustum) const
+{
+    // The box must either intersect or be in the positive half-space of all six planes of the frustum.
+    return (intersects(frustum.getNear()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getFar()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getLeft()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getRight()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getBottom()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getTop()) != Plane::INTERSECTS_BACK);
+}
+
+float BoundingBox::intersects(const Plane& plane) const
+{
+    // Calculate the distance from the center of the box to the plane.
+    Vector3 center((min.x + max.x) * 0.5f, (min.y + max.y) * 0.5f, (min.z + max.z) * 0.5f);
+    float distance = plane.distance(center);
+
+    // Get the extents of the box from its center along each axis.
+    float extentX = (max.x - min.x) * 0.5f;
+    float extentY = (max.y - min.y) * 0.5f;
+    float extentZ = (max.z - min.z) * 0.5f;
+
+    const Vector3& planeNormal = plane.getNormal();
+    if (fabsf(distance) <= (fabsf(extentX * planeNormal.x) + fabsf(extentY * planeNormal.y) + fabsf(
+        extentZ * planeNormal.z)))
+    {
+        return Plane::INTERSECTS_INTERSECTING;
+    }
+
+    return (distance > 0.0f) ? (float)Plane::INTERSECTS_FRONT : (float)Plane::INTERSECTS_BACK;
+}
+
+float BoundingBox::intersects(const Ray& ray) const
+{
+    // Intermediate calculation variables.
+    float dnear = 0.0f;
+    float dfar = 0.0f;
+    float tmin = 0.0f;
+    float tmax = 0.0f;
+
+    const Vector3& origin = ray.getOrigin();
+    const Vector3& direction = ray.getDirection();
+
+    // X direction.
+    float div = 1.0f / direction.x;
+    if (div >= 0.0f)
+    {
+        tmin = (min.x - origin.x) * div;
+        tmax = (max.x - origin.x) * div;
+    }
+    else
+    {
+        tmin = (max.x - origin.x) * div;
+        tmax = (min.x - origin.x) * div;
+    }
+    dnear = tmin;
+    dfar = tmax;
+
+    // Check if the ray misses the box.
+    if (dnear > dfar || dfar < 0.0f)
+    {
+        return Ray::INTERSECTS_NONE;
+    }
+
+    // Y direction.
+    div = 1.0f / direction.y;
+    if (div >= 0.0f)
+    {
+        tmin = (min.y - origin.y) * div;
+        tmax = (max.y - origin.y) * div;
+    }
+    else
+    {
+        tmin = (max.y - origin.y) * div;
+        tmax = (min.y - origin.y) * div;
+    }
+
+    // Update the near and far intersection distances.
+    if (tmin > dnear)
+    {
+        dnear = tmin;
+    }
+    if (tmax < dfar)
+    {
+        dfar = tmax;
+    }
+    // Check if the ray misses the box.
+    if (dnear > dfar || dfar < 0.0f)
+    {
+        return Ray::INTERSECTS_NONE;
+    }
+
+    // Z direction.
+    div = 1.0f / direction.z;
+    if (div >= 0.0f)
+    {
+        tmin = (min.z - origin.z) * div;
+        tmax = (max.z - origin.z) * div;
+    }
+    else
+    {
+        tmin = (max.z - origin.z) * div;
+        tmax = (min.z - origin.z) * div;
+    }
+
+    // Update the near and far intersection distances.
+    if (tmin > dnear)
+    {
+        dnear = tmin;
+    }
+    if (tmax < dfar)
+    {
+        dfar = tmax;
+    }
+
+    // Check if the ray misses the box.
+    if (dnear > dfar || dfar < 0.0f)
+    {
+        return Ray::INTERSECTS_NONE;
+    }
+    // The ray intersects the box (and since the direction of a Ray is normalized, dnear is the distance to the ray).
+    return dnear;
+}
+
+bool BoundingBox::isEmpty() const
+{
+    return min.x == max.x && min.y == max.y && min.z == max.z;
+}
+
+void BoundingBox::merge(const BoundingBox& box)
+{
+    // Calculate the new minimum point.
+    min.x = std::min(min.x, box.min.x);
+    min.y = std::min(min.y, box.min.y);
+    min.z = std::min(min.z, box.min.z);
+
+    // Calculate the new maximum point.
+    max.x = std::max(max.x, box.max.x);
+    max.y = std::max(max.y, box.max.y);
+    max.z = std::max(max.z, box.max.z);
+}
+
+void BoundingBox::merge(const BoundingSphere& sphere)
+{
+    const Vector3& center = sphere.center;
+    float radius = sphere.radius;
+
+    // Calculate the new minimum point for the merged bounding box.
+    min.x = std::min(min.x, center.x - radius);
+    min.y = std::min(min.y, center.y - radius);
+    min.z = std::min(min.z, center.z - radius);
+
+    // Calculate the new maximum point for the merged bounding box.
+    max.x = std::max(max.x, center.x + radius);
+    max.y = std::max(max.y, center.y + radius);
+    max.z = std::max(max.z, center.z + radius);
+}
+
+void BoundingBox::set(const Vector3& min, const Vector3& max)
+{
+    this->min = min;
+    this->max = max;
+}
+
+void BoundingBox::set(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
+{
+    min.set(minX, minY, minZ);
+    max.set(maxX, maxY, maxZ);
+}
+
+static void updateMinMax(Vector3* point, Vector3* min, Vector3* max)
+{
+    GP_ASSERT(point);
+    GP_ASSERT(min);
+    GP_ASSERT(max);
+
+    // Leftmost point.
+    if (point->x < min->x)
+    {
+        min->x = point->x;
+    }
+
+    // Rightmost point.
+    if (point->x > max->x)
+    {
+        max->x = point->x;
+    }
+
+    // Lowest point.
+    if (point->y < min->y)
+    {
+        min->y = point->y;
+    }
+
+    // Highest point.
+    if (point->y > max->y)
+    {
+        max->y = point->y;
+    }
+
+    // Farthest point.
+    if (point->z < min->z)
+    {
+        min->z = point->z;
+    }
+
+    // Nearest point.
+    if (point->z > max->z)
+    {
+        max->z = point->z;
+    }
+}
+
+void BoundingBox::set(const BoundingBox& box)
+{
+    min = box.min;
+    max = box.max;
+}
+
+void BoundingBox::set(const BoundingSphere& sphere)
+{
+    const Vector3& center = sphere.center;
+    float radius = sphere.radius;
+
+    // Calculate the minimum point for the box.
+    min.x = center.x - radius;
+    min.y = center.y - radius;
+    min.z = center.z - radius;
+
+    // Calculate the maximum point for the box.
+    max.x = center.x + radius;
+    max.y = center.y + radius;
+    max.z = center.z + radius;
+}
+
+void BoundingBox::transform(const Matrix& matrix)
+{
+    // Calculate the corners.
+    Vector3 corners[8];
+    getCorners(corners);
+
+    // Transform the corners, recalculating the min and max points along the way.
+    matrix.transformPoint(&corners[0]);
+    Vector3 newMin = corners[0];
+    Vector3 newMax = corners[0];
+    for (int i = 1; i < 8; i++)
+    {
+        matrix.transformPoint(&corners[i]);
+        updateMinMax(&corners[i], &newMin, &newMax);
+    }
+    this->min.x = newMin.x;
+    this->min.y = newMin.y;
+    this->min.z = newMin.z;
+    this->max.x = newMax.x;
+    this->max.y = newMax.y;
+    this->max.z = newMax.z;
+}
+
+}

+ 233 - 233
gameplay/src/BoundingBox.h

@@ -1,233 +1,233 @@
-#ifndef BOUNDINGBOX_H_
-#define BOUNDINGBOX_H_
-
-#include "Frustum.h"
-
-namespace gameplay
-{
-
-/**
- * Defines a 3-dimensional axis-aligned bounding box.
- */
-class BoundingBox
-{
-public:
-
-    /**
-     * The minimum point.
-     */
-    Vector3 min;
-
-    /**
-     * The maximum point.
-     */
-    Vector3 max;
-
-    /**
-     * Constructs an empty bounding box at the origin.
-     */
-    BoundingBox();
-
-    /**
-     * Constructs a new bounding box from the specified values.
-     *
-     * @param min The minimum point of the bounding box.
-     * @param max The maximum point of the bounding box.
-     */
-    BoundingBox(const Vector3& min, const Vector3& max);
-
-    /**
-     * Constructs a new bounding box from the specified values.
-     * 
-     * @param minX The x coordinate of the minimum point of the bounding box.
-     * @param minY The y coordinate of the minimum point of the bounding box.
-     * @param minZ The z coordinate of the minimum point of the bounding box.
-     * @param maxX The x coordinate of the maximum point of the bounding box.
-     * @param maxY The y coordinate of the maximum point of the bounding box.
-     * @param maxZ The z coordinate of the maximum point of the bounding box.
-     */
-    BoundingBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ);
-
-    /**
-     * Constructs a new bounding box from the given bounding box.
-     *
-     * @param copy The bounding box to copy.
-     */
-    BoundingBox(const BoundingBox& copy);
-
-    /**
-     * Destructor.
-     */
-    ~BoundingBox();
-
-    /**
-     * Returns an empty bounding box.
-     */
-    static const BoundingBox& empty();
-
-    /**
-     * Gets the center point of the bounding box.
-     *
-     * This method computes the center point of the box from its min and max.
-     *
-     * @return The center point of the bounding box.
-     */
-    Vector3 getCenter() const;
-
-    /**
-     * Gets the center point of the bounding box.
-     *
-     * This method computes the center point of the box from its min and max
-     * points and stores the result in dst.
-     *
-     * @param dst The vector to store the result in.
-     */
-    void getCenter(Vector3* dst) const;
-
-    /**
-     * Gets the corners of the bounding box in the specified array.
-     *
-     * The corners are returned as follows: 0 to 3 specify the near face starting at the upper left point
-     * when looking towards the origin from the positive z-axis in a counter-clockwise fashion; 4 to 7
-     * specify the far face starting at the upper left point when looking towards the origin from the negative
-     * z-axis in a counter-clockwise fashion.
-     *
-     * @param dst The array to store the corners in. Must be size 8.
-     */
-    void getCorners(Vector3* dst) const;
-
-    /**
-     * Tests whether this bounding box intersects the specified bounding object.
-     *
-     * @param box The bounding box to test intersection with.
-     * 
-     * @return true if the specified bounding box intersects this bounding box; false otherwise.
-     */
-    bool intersects(const BoundingBox& box) const;
-
-    /**
-     * Tests whether this bounding box intersects the specified bounding sphere.
-     *
-     * @param sphere The bounding sphere to test intersection with.
-     * 
-     * @return true if the specified bounding sphere intersects this bounding box; false otherwise.
-     */
-    bool intersects(const BoundingSphere& sphere) const;
-
-    /**
-     * Tests whether this bounding box intersects the specified frustum.
-     *
-     * @param frustum The frustum to test intersection with.
-     * 
-     * @return true if this bounding sphere intersects the specified frustum; false otherwise.
-     */
-    bool intersects(const Frustum& frustum) const;
-
-    /**
-     * Tests whether this bounding box intersects the specified plane.
-     *
-     * @param plane The plane to test intersection with.
-     * 
-     * @return Plane::INTERSECTS_BACK INTERSECTS_BACK if this bounding box is in the negative half-space of
-     *  the plane, Plane::INTERSECTS_FRONT INTERSECTS_FRONT if it is in the positive half-space of the plane;
-     *  and Plane::INTERSECTS_INTERSECTING INTERSECTS_INTERSECTING if it intersects the plane.
-     */
-    float intersects(const Plane& plane) const;
-
-    /**
-     * Tests whether this bounding box intersects the specified ray.
-     *
-     * @param ray The ray to test intersection with.
-     * 
-     * @return The distance from the origin of the ray to this bounding box or
-     *  INTERSECTS_NONE INTERSECTS_NONE if the ray does not intersect this bounding box.
-     */
-    float intersects(const Ray& ray) const;
-
-    /**
-     * Determines if this bounding box is empty.
-     *
-     * @return true if this bounding box is empty; false otherwise.
-     */
-    bool isEmpty() const;
-
-    /**
-     * Sets this bounding box to the smallest bounding box
-     * that contains both this bounding box and the specified bounding sphere.
-     *
-     * @param sphere The bounding sphere to merge with.
-     */
-    void merge(const BoundingSphere& sphere);
-
-    /**
-     * Sets this bounding box to the smallest bounding box
-     * that contains both this bounding object and the specified bounding box.
-     *
-     * @param box The bounding box to merge with.
-     */
-    void merge(const BoundingBox& box);
-
-    /**
-     * Sets this bounding box to the specified values.
-     *
-     * @param min The minimum point of the bounding box.
-     * @param max The maximum point of the bounding box.
-     */
-    void set(const Vector3& min, const Vector3& max);
-
-    /**
-     * Sets this bounding box to the specified values.
-     * 
-     * @param minX The x coordinate of the minimum point of the bounding box.
-     * @param minY The y coordinate of the minimum point of the bounding box.
-     * @param minZ The z coordinate of the minimum point of the bounding box.
-     * @param maxX The x coordinate of the maximum point of the bounding box.
-     * @param maxY The y coordinate of the maximum point of the bounding box.
-     * @param maxZ The z coordinate of the maximum point of the bounding box.
-     */
-    void set(float minX, float minY, float minZ, float maxX, float maxY, float maxZ);
-
-    /**
-     * Sets this bounding box to the specified bounding box.
-     *
-     * @param box The bounding box to set to.
-     */
-    void set(const BoundingBox& box);
-
-    /**
-     * Sets this box to tightly contain the specified bounding sphere.
-     *
-     * @param sphere The sphere to contain.
-     */
-    void set(const BoundingSphere& sphere);
-
-    /**
-     * Transforms the bounding box by the given transformation matrix.
-     *
-     * @param matrix The transformation matrix to transform by.
-     */
-    void transform(const Matrix& matrix);
-
-    /**
-     * Transforms this bounding box by the given matrix.
-     * 
-     * @param matrix The matrix to transform by.
-     * @return This bounding box, after the transformation occurs.
-     */
-    inline BoundingBox& operator*=(const Matrix& matrix);
-};
-
-/**
- * Transforms the given bounding box by the given matrix.
- * 
- * @param matrix The matrix to transform by.
- * @param box The bounding box to transform.
- * @return The resulting transformed bounding box.
- */
-inline const BoundingBox operator*(const Matrix& matrix, const BoundingBox& box);
-
-}
-
-#include "BoundingBox.inl"
-
-#endif
+#ifndef BOUNDINGBOX_H_
+#define BOUNDINGBOX_H_
+
+#include "Frustum.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a 3-dimensional axis-aligned bounding box.
+ */
+class BoundingBox
+{
+public:
+
+    /**
+     * The minimum point.
+     */
+    Vector3 min;
+
+    /**
+     * The maximum point.
+     */
+    Vector3 max;
+
+    /**
+     * Constructs an empty bounding box at the origin.
+     */
+    BoundingBox();
+
+    /**
+     * Constructs a new bounding box from the specified values.
+     *
+     * @param min The minimum point of the bounding box.
+     * @param max The maximum point of the bounding box.
+     */
+    BoundingBox(const Vector3& min, const Vector3& max);
+
+    /**
+     * Constructs a new bounding box from the specified values.
+     * 
+     * @param minX The x coordinate of the minimum point of the bounding box.
+     * @param minY The y coordinate of the minimum point of the bounding box.
+     * @param minZ The z coordinate of the minimum point of the bounding box.
+     * @param maxX The x coordinate of the maximum point of the bounding box.
+     * @param maxY The y coordinate of the maximum point of the bounding box.
+     * @param maxZ The z coordinate of the maximum point of the bounding box.
+     */
+    BoundingBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ);
+
+    /**
+     * Constructs a new bounding box from the given bounding box.
+     *
+     * @param copy The bounding box to copy.
+     */
+    BoundingBox(const BoundingBox& copy);
+
+    /**
+     * Destructor.
+     */
+    ~BoundingBox();
+
+    /**
+     * Returns an empty bounding box.
+     */
+    static const BoundingBox& empty();
+
+    /**
+     * Gets the center point of the bounding box.
+     *
+     * This method computes the center point of the box from its min and max.
+     *
+     * @return The center point of the bounding box.
+     */
+    Vector3 getCenter() const;
+
+    /**
+     * Gets the center point of the bounding box.
+     *
+     * This method computes the center point of the box from its min and max
+     * points and stores the result in dst.
+     *
+     * @param dst The vector to store the result in.
+     */
+    void getCenter(Vector3* dst) const;
+
+    /**
+     * Gets the corners of the bounding box in the specified array.
+     *
+     * The corners are returned as follows: 0 to 3 specify the near face starting at the upper left point
+     * when looking towards the origin from the positive z-axis in a counter-clockwise fashion; 4 to 7
+     * specify the far face starting at the upper left point when looking towards the origin from the negative
+     * z-axis in a counter-clockwise fashion.
+     *
+     * @param dst The array to store the corners in. Must be size 8.
+     */
+    void getCorners(Vector3* dst) const;
+
+    /**
+     * Tests whether this bounding box intersects the specified bounding object.
+     *
+     * @param box The bounding box to test intersection with.
+     * 
+     * @return true if the specified bounding box intersects this bounding box; false otherwise.
+     */
+    bool intersects(const BoundingBox& box) const;
+
+    /**
+     * Tests whether this bounding box intersects the specified bounding sphere.
+     *
+     * @param sphere The bounding sphere to test intersection with.
+     * 
+     * @return true if the specified bounding sphere intersects this bounding box; false otherwise.
+     */
+    bool intersects(const BoundingSphere& sphere) const;
+
+    /**
+     * Tests whether this bounding box intersects the specified frustum.
+     *
+     * @param frustum The frustum to test intersection with.
+     * 
+     * @return true if this bounding sphere intersects the specified frustum; false otherwise.
+     */
+    bool intersects(const Frustum& frustum) const;
+
+    /**
+     * Tests whether this bounding box intersects the specified plane.
+     *
+     * @param plane The plane to test intersection with.
+     * 
+     * @return Plane::INTERSECTS_BACK INTERSECTS_BACK if this bounding box is in the negative half-space of
+     *  the plane, Plane::INTERSECTS_FRONT INTERSECTS_FRONT if it is in the positive half-space of the plane;
+     *  and Plane::INTERSECTS_INTERSECTING INTERSECTS_INTERSECTING if it intersects the plane.
+     */
+    float intersects(const Plane& plane) const;
+
+    /**
+     * Tests whether this bounding box intersects the specified ray.
+     *
+     * @param ray The ray to test intersection with.
+     * 
+     * @return The distance from the origin of the ray to this bounding box or
+     *  INTERSECTS_NONE INTERSECTS_NONE if the ray does not intersect this bounding box.
+     */
+    float intersects(const Ray& ray) const;
+
+    /**
+     * Determines if this bounding box is empty.
+     *
+     * @return true if this bounding box is empty; false otherwise.
+     */
+    bool isEmpty() const;
+
+    /**
+     * Sets this bounding box to the smallest bounding box
+     * that contains both this bounding box and the specified bounding sphere.
+     *
+     * @param sphere The bounding sphere to merge with.
+     */
+    void merge(const BoundingSphere& sphere);
+
+    /**
+     * Sets this bounding box to the smallest bounding box
+     * that contains both this bounding object and the specified bounding box.
+     *
+     * @param box The bounding box to merge with.
+     */
+    void merge(const BoundingBox& box);
+
+    /**
+     * Sets this bounding box to the specified values.
+     *
+     * @param min The minimum point of the bounding box.
+     * @param max The maximum point of the bounding box.
+     */
+    void set(const Vector3& min, const Vector3& max);
+
+    /**
+     * Sets this bounding box to the specified values.
+     * 
+     * @param minX The x coordinate of the minimum point of the bounding box.
+     * @param minY The y coordinate of the minimum point of the bounding box.
+     * @param minZ The z coordinate of the minimum point of the bounding box.
+     * @param maxX The x coordinate of the maximum point of the bounding box.
+     * @param maxY The y coordinate of the maximum point of the bounding box.
+     * @param maxZ The z coordinate of the maximum point of the bounding box.
+     */
+    void set(float minX, float minY, float minZ, float maxX, float maxY, float maxZ);
+
+    /**
+     * Sets this bounding box to the specified bounding box.
+     *
+     * @param box The bounding box to set to.
+     */
+    void set(const BoundingBox& box);
+
+    /**
+     * Sets this box to tightly contain the specified bounding sphere.
+     *
+     * @param sphere The sphere to contain.
+     */
+    void set(const BoundingSphere& sphere);
+
+    /**
+     * Transforms the bounding box by the given transformation matrix.
+     *
+     * @param matrix The transformation matrix to transform by.
+     */
+    void transform(const Matrix& matrix);
+
+    /**
+     * Transforms this bounding box by the given matrix.
+     * 
+     * @param matrix The matrix to transform by.
+     * @return This bounding box, after the transformation occurs.
+     */
+    inline BoundingBox& operator*=(const Matrix& matrix);
+};
+
+/**
+ * Transforms the given bounding box by the given matrix.
+ * 
+ * @param matrix The matrix to transform by.
+ * @param box The bounding box to transform.
+ * @return The resulting transformed bounding box.
+ */
+inline const BoundingBox operator*(const Matrix& matrix, const BoundingBox& box);
+
+}
+
+#include "BoundingBox.inl"
+
+#endif

+ 19 - 19
gameplay/src/BoundingBox.inl

@@ -1,19 +1,19 @@
-#include "BoundingBox.h"
-
-namespace gameplay
-{
-
-inline BoundingBox& BoundingBox::operator*=(const Matrix& matrix)
-{
-    transform(matrix);
-    return *this;
-}
-
-inline const BoundingBox operator*(const Matrix& matrix, const BoundingBox& box)
-{
-    BoundingBox b(box);
-    b.transform(matrix);
-    return b;
-}
-
-}
+#include "BoundingBox.h"
+
+namespace gameplay
+{
+
+inline BoundingBox& BoundingBox::operator*=(const Matrix& matrix)
+{
+    transform(matrix);
+    return *this;
+}
+
+inline const BoundingBox operator*(const Matrix& matrix, const BoundingBox& box)
+{
+    BoundingBox b(box);
+    b.transform(matrix);
+    return b;
+}
+
+}

+ 324 - 324
gameplay/src/BoundingSphere.cpp

@@ -1,324 +1,324 @@
-#include "Base.h"
-#include "BoundingSphere.h"
-#include "BoundingBox.h"
-
-namespace gameplay
-{
-
-BoundingSphere::BoundingSphere()
-    : radius(0)
-{
-}
-
-BoundingSphere::BoundingSphere(const Vector3& center, float radius)
-{
-    set(center, radius);
-}
-
-BoundingSphere::BoundingSphere(const BoundingSphere& copy)
-{
-    set(copy);
-}
-
-BoundingSphere::~BoundingSphere()
-{
-}
-
-const BoundingSphere& BoundingSphere::empty()
-{
-    static BoundingSphere s;
-    return s;
-}
-
-bool BoundingSphere::intersects(const BoundingSphere& sphere) const
-{
-    // If the distance between the spheres' centers is less than or equal
-    // to the sum of their radii, then the spheres intersect.
-    float vx = sphere.center.x - center.x;
-    float vy = sphere.center.y - center.y;
-    float vz = sphere.center.z - center.z;
-
-    return sqrt(vx * vx + vy * vy + vz * vz) <= (radius + sphere.radius);
-}
-
-bool BoundingSphere::intersects(const BoundingBox& box) const
-{
-    // Determine what point is closest; if the distance to that
-    // point is less than the radius, then this sphere intersects.
-    float cpX = center.x;
-    float cpY = center.y;
-    float cpZ = center.z;
-
-    const Vector3& boxMin = box.min;
-    const Vector3& boxMax = box.max;
-    // Closest x value.
-    if (center.x < boxMin.x)
-    {
-        cpX = boxMin.x;
-    }
-    else if (center.x > boxMax.x)
-    {
-        cpX = boxMax.x;
-    }
-
-    // Closest y value.
-    if (center.y < boxMin.y)
-    {
-        cpY = boxMin.y;
-    }
-    else if (center.y > boxMax.y)
-    {
-        cpY = boxMax.y;
-    }
-
-    // Closest z value.
-    if (center.z < boxMin.z)
-    {
-        cpZ = boxMin.z;
-    }
-    else if (center.z > boxMax.z)
-    {
-        cpZ = boxMax.z;
-    }
-
-    // Find the distance to the closest point and see if it is less than or equal to the radius.
-    cpX -= center.x;
-    cpY -= center.y;
-    cpZ -= center.z;
-
-    return sqrt(cpX * cpX + cpY * cpY + cpZ * cpZ) <= radius;
-}
-
-bool BoundingSphere::intersects(const Frustum& frustum) const
-{
-    // The sphere must either intersect or be in the positive half-space of all six planes of the frustum.
-    return (intersects(frustum.getNear()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getFar()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getLeft()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getRight()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getBottom()) != Plane::INTERSECTS_BACK &&
-            intersects(frustum.getTop()) != Plane::INTERSECTS_BACK );
-}
-
-float BoundingSphere::intersects(const Plane& plane) const
-{
-    float distance = plane.distance(center);
-
-    if (fabsf(distance) <= radius)
-    {
-        return Plane::INTERSECTS_INTERSECTING;
-    }
-    else if (distance > 0.0f)
-    {
-        return Plane::INTERSECTS_FRONT;
-    }
-    else
-    {
-        return Plane::INTERSECTS_BACK;
-    }
-}
-
-float BoundingSphere::intersects(const Ray& ray) const
-{
-    const Vector3& origin = ray.getOrigin();
-    const Vector3& direction = ray.getDirection();
-
-    // Calculate the vector and the square of the distance from the ray's origin to this sphere's center.
-    float vx = origin.x - center.x;
-    float vy = origin.y - center.y;
-    float vz = origin.z - center.z;
-    float d2 = vx * vx + vy * vy + vz * vz;
-
-    // Solve the quadratic equation using the ray's and sphere's equations together.
-    // Since the ray's direction is guaranteed to be 1 by the Ray, we don't need to
-    // calculate and use A (A=ray.getDirection().lengthSquared()).
-    float B = 2.0f * (vx * direction.x + vy * direction.y + vz * direction.z);
-    float C = d2 - radius * radius;
-    float discriminant = B * B - 4.0f * C;
-
-    // If the discriminant is negative, then there is no intersection.
-    if (discriminant < 0.0f)
-    {
-        return Ray::INTERSECTS_NONE;
-    }
-    else
-    {
-        // The intersection is at the smaller positive root.
-        float sqrtDisc = sqrt(discriminant);
-        float t0 = (-B - sqrtDisc) * 0.5f;
-        float t1 = (-B + sqrtDisc) * 0.5f;
-        return (t0 > 0.0f && t0 < t1) ? t0 : t1;
-    }
-}
-
-bool BoundingSphere::isEmpty() const
-{
-    return radius == 0.0f;
-}
-
-void BoundingSphere::merge(const BoundingSphere& sphere)
-{
-    if (sphere.isEmpty())
-        return;
-
-    // Calculate the distance between the two centers.
-    float vx = center.x - sphere.center.x;
-    float vy = center.y - sphere.center.y;
-    float vz = center.z - sphere.center.z;
-    float d = sqrt(vx * vx + vy * vy + vz * vz);
-
-    // If one sphere is contained inside the other, set to the larger sphere.
-    if (d <= (sphere.radius - radius))
-    {
-        center = sphere.center;
-        radius = sphere.radius;
-        return;
-    }
-    else if (d <= (radius - sphere.radius))
-    {
-        return;
-    }
-
-    // Calculate the unit vector between the two centers.
-    GP_ASSERT(d != 0.0f);
-    float dI = 1.0f / d;
-    vx *= dI;
-    vy *= dI;
-    vz *= dI;
-
-    // Calculate the new radius.
-    float r = (radius + sphere.radius + d) * 0.5f;
-
-    // Calculate the new center.
-    float scaleFactor = (r - sphere.radius);
-    vx = vx * scaleFactor + sphere.center.x;
-    vy = vy * scaleFactor + sphere.center.y;
-    vz = vz * scaleFactor + sphere.center.z;
-
-    // Set the new center and radius.
-    center.x = vx;
-    center.y = vy;
-    center.z = vz;
-    radius = r;
-}
-
-void BoundingSphere::merge(const BoundingBox& box)
-{
-    if (box.isEmpty())
-        return;
-
-    const Vector3& min = box.min;
-    const Vector3& max = box.max;
-
-    // Find the corner of the bounding box that is farthest away from this sphere's center.
-    float v1x = min.x - center.x;
-    float v1y = min.y - center.y;
-    float v1z = min.z - center.z;
-    float v2x = max.x - center.x;
-    float v2y = max.y - center.y;
-    float v2z = max.z - center.z;
-    float fx = min.x;
-    float fy = min.y;
-    float fz = min.z;
-
-    if (v2x > v1x)
-    {
-        fx = max.x;
-    }
-    if (v2y > v1y)
-    {
-        fy = max.y;
-    }
-    if (v2z > v1z)
-    {
-        fz = max.z;
-    }
-
-    // Calculate the unit vector and the distance between the center and the farthest point.
-    v1x = center.x - fx;
-    v1y = center.y - fy;
-    v1z = center.z - fz;
-    float distance = sqrt(v1x * v1x + v1y * v1y + v1z * v1z);
-
-    // If the box is inside the sphere, we are done.
-    if (distance <= radius)
-    {
-        return;
-    }
-
-    // Calculate the unit vector between the center and the farthest point.
-    GP_ASSERT(distance != 0.0f);
-    float dI = 1.0f / distance;
-    v1x *= dI;
-    v1y *= dI;
-    v1z *= dI;
-
-    // Calculate the new radius.
-    float r = (radius + distance) * 0.5f;
-
-    // Calculate the new center.
-    v1x = v1x * r + fx;
-    v1y = v1y * r + fy;
-    v1z = v1z * r + fz;
-
-    // Set the new center and radius.
-    center.x = v1x;
-    center.y = v1y;
-    center.z = v1z;
-    radius = r;
-}
-
-void BoundingSphere::set(const Vector3& center, float radius)
-{
-    this->center = center;
-    this->radius = radius;
-}
-
-void BoundingSphere::set(const BoundingSphere& sphere)
-{
-    center = sphere.center;
-    radius = sphere.radius;
-}
-
-void BoundingSphere::set(const BoundingBox& box)
-{
-    center.x = (box.min.x + box.max.x) * 0.5f;
-    center.y = (box.min.y + box.max.y) * 0.5f;
-    center.z = (box.min.z + box.max.z) * 0.5f;
-    radius = center.distance(box.max);
-}
-
-void BoundingSphere::transform(const Matrix& matrix)
-{
-    // Translate the center point.
-    matrix.transformPoint(center, &center);
-
-    // Scale the sphere's radius by the scale fo the matrix
-    Vector3 scale;
-    matrix.decompose(&scale, NULL, NULL);
-    float r = radius * scale.x;
-    r = max(r, radius * scale.y);
-    r = max(r, radius * scale.z);
-    radius = r;
-}
-
-float BoundingSphere::distance(const BoundingSphere& sphere, const Vector3& point)
-{
-    return sqrt((point.x - sphere.center.x) * (point.x - sphere.center.x) +
-                 (point.y - sphere.center.y) * (point.y - sphere.center.x) +
-                 (point.z - sphere.center.z) * (point.z - sphere.center.x));
-}
-
-bool BoundingSphere::contains(const BoundingSphere& sphere, Vector3* points, unsigned int count)
-{
-    for (unsigned int i = 0; i < count; i++)
-    {
-        if (distance(sphere, points[i]) > sphere.radius)
-        {
-            return false;
-        }
-    }
-    return true;
-}
-
-}
+#include "Base.h"
+#include "BoundingSphere.h"
+#include "BoundingBox.h"
+
+namespace gameplay
+{
+
+BoundingSphere::BoundingSphere()
+    : radius(0)
+{
+}
+
+BoundingSphere::BoundingSphere(const Vector3& center, float radius)
+{
+    set(center, radius);
+}
+
+BoundingSphere::BoundingSphere(const BoundingSphere& copy)
+{
+    set(copy);
+}
+
+BoundingSphere::~BoundingSphere()
+{
+}
+
+const BoundingSphere& BoundingSphere::empty()
+{
+    static BoundingSphere s;
+    return s;
+}
+
+bool BoundingSphere::intersects(const BoundingSphere& sphere) const
+{
+    // If the distance between the spheres' centers is less than or equal
+    // to the sum of their radii, then the spheres intersect.
+    float vx = sphere.center.x - center.x;
+    float vy = sphere.center.y - center.y;
+    float vz = sphere.center.z - center.z;
+
+    return sqrt(vx * vx + vy * vy + vz * vz) <= (radius + sphere.radius);
+}
+
+bool BoundingSphere::intersects(const BoundingBox& box) const
+{
+    // Determine what point is closest; if the distance to that
+    // point is less than the radius, then this sphere intersects.
+    float cpX = center.x;
+    float cpY = center.y;
+    float cpZ = center.z;
+
+    const Vector3& boxMin = box.min;
+    const Vector3& boxMax = box.max;
+    // Closest x value.
+    if (center.x < boxMin.x)
+    {
+        cpX = boxMin.x;
+    }
+    else if (center.x > boxMax.x)
+    {
+        cpX = boxMax.x;
+    }
+
+    // Closest y value.
+    if (center.y < boxMin.y)
+    {
+        cpY = boxMin.y;
+    }
+    else if (center.y > boxMax.y)
+    {
+        cpY = boxMax.y;
+    }
+
+    // Closest z value.
+    if (center.z < boxMin.z)
+    {
+        cpZ = boxMin.z;
+    }
+    else if (center.z > boxMax.z)
+    {
+        cpZ = boxMax.z;
+    }
+
+    // Find the distance to the closest point and see if it is less than or equal to the radius.
+    cpX -= center.x;
+    cpY -= center.y;
+    cpZ -= center.z;
+
+    return sqrt(cpX * cpX + cpY * cpY + cpZ * cpZ) <= radius;
+}
+
+bool BoundingSphere::intersects(const Frustum& frustum) const
+{
+    // The sphere must either intersect or be in the positive half-space of all six planes of the frustum.
+    return (intersects(frustum.getNear()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getFar()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getLeft()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getRight()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getBottom()) != Plane::INTERSECTS_BACK &&
+            intersects(frustum.getTop()) != Plane::INTERSECTS_BACK );
+}
+
+float BoundingSphere::intersects(const Plane& plane) const
+{
+    float distance = plane.distance(center);
+
+    if (fabsf(distance) <= radius)
+    {
+        return Plane::INTERSECTS_INTERSECTING;
+    }
+    else if (distance > 0.0f)
+    {
+        return Plane::INTERSECTS_FRONT;
+    }
+    else
+    {
+        return Plane::INTERSECTS_BACK;
+    }
+}
+
+float BoundingSphere::intersects(const Ray& ray) const
+{
+    const Vector3& origin = ray.getOrigin();
+    const Vector3& direction = ray.getDirection();
+
+    // Calculate the vector and the square of the distance from the ray's origin to this sphere's center.
+    float vx = origin.x - center.x;
+    float vy = origin.y - center.y;
+    float vz = origin.z - center.z;
+    float d2 = vx * vx + vy * vy + vz * vz;
+
+    // Solve the quadratic equation using the ray's and sphere's equations together.
+    // Since the ray's direction is guaranteed to be 1 by the Ray, we don't need to
+    // calculate and use A (A=ray.getDirection().lengthSquared()).
+    float B = 2.0f * (vx * direction.x + vy * direction.y + vz * direction.z);
+    float C = d2 - radius * radius;
+    float discriminant = B * B - 4.0f * C;
+
+    // If the discriminant is negative, then there is no intersection.
+    if (discriminant < 0.0f)
+    {
+        return Ray::INTERSECTS_NONE;
+    }
+    else
+    {
+        // The intersection is at the smaller positive root.
+        float sqrtDisc = sqrt(discriminant);
+        float t0 = (-B - sqrtDisc) * 0.5f;
+        float t1 = (-B + sqrtDisc) * 0.5f;
+        return (t0 > 0.0f && t0 < t1) ? t0 : t1;
+    }
+}
+
+bool BoundingSphere::isEmpty() const
+{
+    return radius == 0.0f;
+}
+
+void BoundingSphere::merge(const BoundingSphere& sphere)
+{
+    if (sphere.isEmpty())
+        return;
+
+    // Calculate the distance between the two centers.
+    float vx = center.x - sphere.center.x;
+    float vy = center.y - sphere.center.y;
+    float vz = center.z - sphere.center.z;
+    float d = sqrt(vx * vx + vy * vy + vz * vz);
+
+    // If one sphere is contained inside the other, set to the larger sphere.
+    if (d <= (sphere.radius - radius))
+    {
+        center = sphere.center;
+        radius = sphere.radius;
+        return;
+    }
+    else if (d <= (radius - sphere.radius))
+    {
+        return;
+    }
+
+    // Calculate the unit vector between the two centers.
+    GP_ASSERT(d != 0.0f);
+    float dI = 1.0f / d;
+    vx *= dI;
+    vy *= dI;
+    vz *= dI;
+
+    // Calculate the new radius.
+    float r = (radius + sphere.radius + d) * 0.5f;
+
+    // Calculate the new center.
+    float scaleFactor = (r - sphere.radius);
+    vx = vx * scaleFactor + sphere.center.x;
+    vy = vy * scaleFactor + sphere.center.y;
+    vz = vz * scaleFactor + sphere.center.z;
+
+    // Set the new center and radius.
+    center.x = vx;
+    center.y = vy;
+    center.z = vz;
+    radius = r;
+}
+
+void BoundingSphere::merge(const BoundingBox& box)
+{
+    if (box.isEmpty())
+        return;
+
+    const Vector3& min = box.min;
+    const Vector3& max = box.max;
+
+    // Find the corner of the bounding box that is farthest away from this sphere's center.
+    float v1x = min.x - center.x;
+    float v1y = min.y - center.y;
+    float v1z = min.z - center.z;
+    float v2x = max.x - center.x;
+    float v2y = max.y - center.y;
+    float v2z = max.z - center.z;
+    float fx = min.x;
+    float fy = min.y;
+    float fz = min.z;
+
+    if (v2x > v1x)
+    {
+        fx = max.x;
+    }
+    if (v2y > v1y)
+    {
+        fy = max.y;
+    }
+    if (v2z > v1z)
+    {
+        fz = max.z;
+    }
+
+    // Calculate the unit vector and the distance between the center and the farthest point.
+    v1x = center.x - fx;
+    v1y = center.y - fy;
+    v1z = center.z - fz;
+    float distance = sqrt(v1x * v1x + v1y * v1y + v1z * v1z);
+
+    // If the box is inside the sphere, we are done.
+    if (distance <= radius)
+    {
+        return;
+    }
+
+    // Calculate the unit vector between the center and the farthest point.
+    GP_ASSERT(distance != 0.0f);
+    float dI = 1.0f / distance;
+    v1x *= dI;
+    v1y *= dI;
+    v1z *= dI;
+
+    // Calculate the new radius.
+    float r = (radius + distance) * 0.5f;
+
+    // Calculate the new center.
+    v1x = v1x * r + fx;
+    v1y = v1y * r + fy;
+    v1z = v1z * r + fz;
+
+    // Set the new center and radius.
+    center.x = v1x;
+    center.y = v1y;
+    center.z = v1z;
+    radius = r;
+}
+
+void BoundingSphere::set(const Vector3& center, float radius)
+{
+    this->center = center;
+    this->radius = radius;
+}
+
+void BoundingSphere::set(const BoundingSphere& sphere)
+{
+    center = sphere.center;
+    radius = sphere.radius;
+}
+
+void BoundingSphere::set(const BoundingBox& box)
+{
+    center.x = (box.min.x + box.max.x) * 0.5f;
+    center.y = (box.min.y + box.max.y) * 0.5f;
+    center.z = (box.min.z + box.max.z) * 0.5f;
+    radius = center.distance(box.max);
+}
+
+void BoundingSphere::transform(const Matrix& matrix)
+{
+    // Translate the center point.
+    matrix.transformPoint(center, &center);
+
+    // Scale the sphere's radius by the scale fo the matrix
+    Vector3 scale;
+    matrix.decompose(&scale, NULL, NULL);
+    float r = radius * scale.x;
+    r = max(r, radius * scale.y);
+    r = max(r, radius * scale.z);
+    radius = r;
+}
+
+float BoundingSphere::distance(const BoundingSphere& sphere, const Vector3& point)
+{
+    return sqrt((point.x - sphere.center.x) * (point.x - sphere.center.x) +
+                 (point.y - sphere.center.y) * (point.y - sphere.center.x) +
+                 (point.z - sphere.center.z) * (point.z - sphere.center.x));
+}
+
+bool BoundingSphere::contains(const BoundingSphere& sphere, Vector3* points, unsigned int count)
+{
+    for (unsigned int i = 0; i < count; i++)
+    {
+        if (distance(sphere, points[i]) > sphere.radius)
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+}

+ 184 - 184
gameplay/src/BoundingSphere.h

@@ -1,184 +1,184 @@
-#ifndef BOUNDINGSPHERE_H_
-#define BOUNDINGSPHERE_H_
-
-#include "Frustum.h"
-
-namespace gameplay
-{
-
-/**
- * Defines a 3-dimensional bounding sphere.
- */
-class BoundingSphere
-{
-public:
-
-    /**
-     * The center point.
-     */
-    Vector3 center;
-
-    /**
-     * The sphere radius.
-     */
-    float radius;
-
-    /**
-     *  Constructs a new bounding sphere initialized to all zeros.
-     */
-    BoundingSphere();
-
-    /**
-     * Constructs a new bounding sphere initialized to the specified values.
-     *
-     * @param center The center of the sphere.
-     * @param radius The radius of the sphere.
-     */
-    BoundingSphere(const Vector3& center, float radius);
-
-    /**
-     * Constructs a bounding sphere from the given bounding sphere.
-     *
-     * @param copy The bounding sphere to copy.
-     */
-    BoundingSphere(const BoundingSphere& copy);
-
-    /**
-     * Destructor.
-     */
-    ~BoundingSphere();
-
-    /**
-     * Returns an empty bounding sphere.
-     */
-    static const BoundingSphere& empty();
-
-    /**
-     * Tests whether this bounding sphere intersects the specified bounding sphere.
-     *
-     * @param sphere The bounding sphere to test intersection with.
-     * 
-     * @return true if the specified bounding sphere intersects this bounding sphere; false otherwise.
-     */
-    bool intersects(const BoundingSphere& sphere) const;
-
-    /**
-     * Tests whether this bounding sphere intersects the specified bounding box.
-     *
-     * @param box The bounding box to test intersection with.
-     * 
-     * @return true if the specified bounding box intersects this bounding sphere; false otherwise.
-     */
-    bool intersects(const BoundingBox& box) const;
-
-    /**
-     * Tests whether this bounding sphere intersects the specified frustum.
-     *
-     * @param frustum The frustum to test intersection with.
-     * 
-     * @return true if this bounding sphere intersects the specified frustum; false otherwise.
-     */
-    bool intersects(const Frustum& frustum) const;
-
-    /**
-     * Tests whether this bounding sphere intersects the specified plane.
-     *
-     * @param plane The plane to test intersection with.
-     * 
-     * @return Plane::INTERSECTS_BACK INTERSECTS_BACK if this bounding sphere is in the negative half-space of
-     *  the plane, Plane::INTERSECTS_FRONT INTERSECTS_FRONT if it is in the positive half-space of the plane,
-     *  and Plane::INTERSECTS_INTERSECTING INTERSECTS_INTERSECTING if it intersects the plane.
-     */
-    float intersects(const Plane& plane) const;
-
-    /**
-     * Tests whether this bounding sphere intersects the specified ray.
-     *
-     * @param ray The ray to test intersection with.
-     * 
-     * @return The distance from the origin of the ray to this bounding sphere or
-     *  Ray::INTERSECTS_NONE INTERSECTS_NONE if the ray does not intersect this bounding sphere.
-     */
-    float intersects(const Ray& ray) const;
-
-    /**
-     * Determines if this bounding sphere is empty.
-     *
-     * @return true if this bounding sphere is empty; false otherwise.
-     */
-    bool isEmpty() const;
-
-    /**
-     * Sets this bounding sphere to the smallest bounding sphere
-     * that contains both this bounding sphere and the specified bounding sphere.
-     *
-     * @param sphere The bounding sphere to merge with.
-     */
-    void merge(const BoundingSphere& sphere);
-
-    /**
-     * Sets this bounding sphere to the smallest bounding sphere
-     * that contains both this bounding sphere and the specified bounding box.
-     *
-     * @param box The bounding box to merge with.
-     */
-    void merge(const BoundingBox& box);
-
-    /**
-     * Sets this bounding sphere to the specified values.
-     *
-     * @param center The center of the sphere.
-     * @param radius The radius of the sphere.
-     */
-    void set(const Vector3& center, float radius);
-
-    /**
-     * Sets this bounding sphere to the specified bounding sphere.
-     *
-     * @param sphere The bounding sphere to set to.
-     */
-    void set(const BoundingSphere& sphere);
-
-    /**
-     * Sets this bounding sphere to tightly contain the specified bounding box.
-     *
-     * @param box The box to contain.
-     */
-    void set(const BoundingBox& box);
-
-    /**
-     * Transforms the bounding sphere by the given transformation matrix.
-     *
-     * @param matrix The transformation matrix to transform by.
-     */
-    void transform(const Matrix& matrix);
-
-    /**
-     * Transforms this bounding sphere by the given matrix.
-     * 
-     * @param matrix The matrix to transform by.
-     * @return This bounding sphere, after the transformation occurs.
-     */
-    inline BoundingSphere& operator*=(const Matrix& matrix);
-
-private:
-
-    float distance(const BoundingSphere& sphere, const Vector3&);
-
-    bool contains(const BoundingSphere& sphere, Vector3* points, unsigned int count);
-};
-
-/**
- * Transforms the given bounding sphere by the given matrix.
- * 
- * @param matrix The matrix to transform by.
- * @param sphere The bounding sphere to transform.
- * @return The resulting transformed bounding sphere.
- */
-inline const BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere);
-
-}
-
-#include "BoundingSphere.inl"
-
-#endif
+#ifndef BOUNDINGSPHERE_H_
+#define BOUNDINGSPHERE_H_
+
+#include "Frustum.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a 3-dimensional bounding sphere.
+ */
+class BoundingSphere
+{
+public:
+
+    /**
+     * The center point.
+     */
+    Vector3 center;
+
+    /**
+     * The sphere radius.
+     */
+    float radius;
+
+    /**
+     *  Constructs a new bounding sphere initialized to all zeros.
+     */
+    BoundingSphere();
+
+    /**
+     * Constructs a new bounding sphere initialized to the specified values.
+     *
+     * @param center The center of the sphere.
+     * @param radius The radius of the sphere.
+     */
+    BoundingSphere(const Vector3& center, float radius);
+
+    /**
+     * Constructs a bounding sphere from the given bounding sphere.
+     *
+     * @param copy The bounding sphere to copy.
+     */
+    BoundingSphere(const BoundingSphere& copy);
+
+    /**
+     * Destructor.
+     */
+    ~BoundingSphere();
+
+    /**
+     * Returns an empty bounding sphere.
+     */
+    static const BoundingSphere& empty();
+
+    /**
+     * Tests whether this bounding sphere intersects the specified bounding sphere.
+     *
+     * @param sphere The bounding sphere to test intersection with.
+     * 
+     * @return true if the specified bounding sphere intersects this bounding sphere; false otherwise.
+     */
+    bool intersects(const BoundingSphere& sphere) const;
+
+    /**
+     * Tests whether this bounding sphere intersects the specified bounding box.
+     *
+     * @param box The bounding box to test intersection with.
+     * 
+     * @return true if the specified bounding box intersects this bounding sphere; false otherwise.
+     */
+    bool intersects(const BoundingBox& box) const;
+
+    /**
+     * Tests whether this bounding sphere intersects the specified frustum.
+     *
+     * @param frustum The frustum to test intersection with.
+     * 
+     * @return true if this bounding sphere intersects the specified frustum; false otherwise.
+     */
+    bool intersects(const Frustum& frustum) const;
+
+    /**
+     * Tests whether this bounding sphere intersects the specified plane.
+     *
+     * @param plane The plane to test intersection with.
+     * 
+     * @return Plane::INTERSECTS_BACK INTERSECTS_BACK if this bounding sphere is in the negative half-space of
+     *  the plane, Plane::INTERSECTS_FRONT INTERSECTS_FRONT if it is in the positive half-space of the plane,
+     *  and Plane::INTERSECTS_INTERSECTING INTERSECTS_INTERSECTING if it intersects the plane.
+     */
+    float intersects(const Plane& plane) const;
+
+    /**
+     * Tests whether this bounding sphere intersects the specified ray.
+     *
+     * @param ray The ray to test intersection with.
+     * 
+     * @return The distance from the origin of the ray to this bounding sphere or
+     *  Ray::INTERSECTS_NONE INTERSECTS_NONE if the ray does not intersect this bounding sphere.
+     */
+    float intersects(const Ray& ray) const;
+
+    /**
+     * Determines if this bounding sphere is empty.
+     *
+     * @return true if this bounding sphere is empty; false otherwise.
+     */
+    bool isEmpty() const;
+
+    /**
+     * Sets this bounding sphere to the smallest bounding sphere
+     * that contains both this bounding sphere and the specified bounding sphere.
+     *
+     * @param sphere The bounding sphere to merge with.
+     */
+    void merge(const BoundingSphere& sphere);
+
+    /**
+     * Sets this bounding sphere to the smallest bounding sphere
+     * that contains both this bounding sphere and the specified bounding box.
+     *
+     * @param box The bounding box to merge with.
+     */
+    void merge(const BoundingBox& box);
+
+    /**
+     * Sets this bounding sphere to the specified values.
+     *
+     * @param center The center of the sphere.
+     * @param radius The radius of the sphere.
+     */
+    void set(const Vector3& center, float radius);
+
+    /**
+     * Sets this bounding sphere to the specified bounding sphere.
+     *
+     * @param sphere The bounding sphere to set to.
+     */
+    void set(const BoundingSphere& sphere);
+
+    /**
+     * Sets this bounding sphere to tightly contain the specified bounding box.
+     *
+     * @param box The box to contain.
+     */
+    void set(const BoundingBox& box);
+
+    /**
+     * Transforms the bounding sphere by the given transformation matrix.
+     *
+     * @param matrix The transformation matrix to transform by.
+     */
+    void transform(const Matrix& matrix);
+
+    /**
+     * Transforms this bounding sphere by the given matrix.
+     * 
+     * @param matrix The matrix to transform by.
+     * @return This bounding sphere, after the transformation occurs.
+     */
+    inline BoundingSphere& operator*=(const Matrix& matrix);
+
+private:
+
+    float distance(const BoundingSphere& sphere, const Vector3&);
+
+    bool contains(const BoundingSphere& sphere, Vector3* points, unsigned int count);
+};
+
+/**
+ * Transforms the given bounding sphere by the given matrix.
+ * 
+ * @param matrix The matrix to transform by.
+ * @param sphere The bounding sphere to transform.
+ * @return The resulting transformed bounding sphere.
+ */
+inline const BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere);
+
+}
+
+#include "BoundingSphere.inl"
+
+#endif

+ 19 - 19
gameplay/src/BoundingSphere.inl

@@ -1,19 +1,19 @@
-#include "BoundingSphere.h"
-
-namespace gameplay
-{
-
-inline BoundingSphere& BoundingSphere::operator*=(const Matrix& matrix)
-{
-    transform(matrix);
-    return *this;
-}
-
-inline const BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere)
-{
-    BoundingSphere s(sphere);
-    s.transform(matrix);
-    return s;
-}
-
-}
+#include "BoundingSphere.h"
+
+namespace gameplay
+{
+
+inline BoundingSphere& BoundingSphere::operator*=(const Matrix& matrix)
+{
+    transform(matrix);
+    return *this;
+}
+
+inline const BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere)
+{
+    BoundingSphere s(sphere);
+    s.transform(matrix);
+    return s;
+}
+
+}

+ 1860 - 1805
gameplay/src/Bundle.cpp

@@ -1,1805 +1,1860 @@
-#include "Base.h"
-#include "Bundle.h"
-#include "FileSystem.h"
-#include "MeshPart.h"
-#include "Scene.h"
-#include "Joint.h"
-
-#define BUNDLE_VERSION_MAJOR            1
-#define BUNDLE_VERSION_MINOR            2
-
-#define BUNDLE_TYPE_SCENE               1
-#define BUNDLE_TYPE_NODE                2
-#define BUNDLE_TYPE_ANIMATIONS          3
-#define BUNDLE_TYPE_ANIMATION           4
-#define BUNDLE_TYPE_ANIMATION_CHANNEL   5
-#define BUNDLE_TYPE_MODEL               10
-#define BUNDLE_TYPE_MATERIAL            16
-#define BUNDLE_TYPE_EFFECT              18
-#define BUNDLE_TYPE_CAMERA              32
-#define BUNDLE_TYPE_LIGHT               33
-#define BUNDLE_TYPE_MESH                34
-#define BUNDLE_TYPE_MESHPART            35
-#define BUNDLE_TYPE_MESHSKIN            36
-#define BUNDLE_TYPE_FONT                128
-
-// For sanity checking string reads
-#define BUNDLE_MAX_STRING_LENGTH        5000
-
-namespace gameplay
-{
-
-static std::vector<Bundle*> __bundleCache;
-
-Bundle::Bundle(const char* path) :
-    _path(path), _referenceCount(0), _references(NULL), _stream(NULL), _trackedNodes(NULL)
-{
-}
-
-Bundle::~Bundle()
-{
-    clearLoadSession();
-
-    // Remove this Bundle from the cache.
-    std::vector<Bundle*>::iterator itr = std::find(__bundleCache.begin(), __bundleCache.end(), this);
-    if (itr != __bundleCache.end())
-    {
-        __bundleCache.erase(itr);
-    }
-
-    SAFE_DELETE_ARRAY(_references);
-
-    if (_stream)
-    {
-        SAFE_DELETE(_stream);
-    }
-}
-
-template <class T>
-bool Bundle::readArray(unsigned int* length, T** ptr)
-{
-    GP_ASSERT(length);
-    GP_ASSERT(ptr);
-    GP_ASSERT(_stream);
-
-    if (!read(length))
-    {
-        GP_ERROR("Failed to read the length of an array of data (to be read into an array).");
-        return false;
-    }
-    if (*length > 0)
-    {
-        *ptr = new T[*length];
-        if (_stream->read(*ptr, sizeof(T), *length) != *length)
-        {
-            GP_ERROR("Failed to read an array of data from bundle (into an array).");
-            SAFE_DELETE_ARRAY(*ptr);
-            return false;
-        }
-    }
-    return true;
-}
-
-template <class T>
-bool Bundle::readArray(unsigned int* length, std::vector<T>* values)
-{
-    GP_ASSERT(length);
-    GP_ASSERT(_stream);
-
-    if (!read(length))
-    {
-        GP_ERROR("Failed to read the length of an array of data (to be read into a std::vector).");
-        return false;
-    }
-    if (*length > 0 && values)
-    {
-        values->resize(*length);
-        if (_stream->read(&(*values)[0], sizeof(T), *length) != *length)
-        {
-            GP_ERROR("Failed to read an array of data from bundle (into a std::vector).");
-            return false;
-        }
-    }
-    return true;
-}
-
-template <class T>
-bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize)
-{
-    GP_ASSERT(length);
-    GP_ASSERT(_stream);
-    GP_ASSERT(sizeof(T) >= readSize);
-
-    if (!read(length))
-    {
-        GP_ERROR("Failed to read the length of an array of data (to be read into a std::vector with a specified single element read size).");
-        return false;
-    }
-    if (*length > 0 && values)
-    {
-        values->resize(*length);
-        if (_stream->read(&(*values)[0], readSize, *length) != *length)
-        {
-            GP_ERROR("Failed to read an array of data from bundle (into a std::vector with a specified single element read size).");
-            return false;
-        }
-    }
-    return true;
-}
-
-static std::string readString(Stream* stream)
-{
-    GP_ASSERT(stream);
-
-    unsigned int length;
-    if (stream->read(&length, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to read the length of a string from a bundle.");
-        return std::string();
-    }
-
-    // Sanity check to detect if string length is far too big.
-    GP_ASSERT(length < BUNDLE_MAX_STRING_LENGTH);
-
-    std::string str;
-    if (length > 0)
-    {
-        str.resize(length);
-        if (stream->read(&str[0], 1, length) != length)
-        {
-            GP_ERROR("Failed to read string from bundle.");
-            return std::string();
-        }
-    }
-    return str;
-}
-
-Bundle* Bundle::create(const char* path)
-{
-    GP_ASSERT(path);
-
-    // Search the cache for this bundle.
-    for (size_t i = 0, count = __bundleCache.size(); i < count; ++i)
-    {
-        Bundle* p = __bundleCache[i];
-        GP_ASSERT(p);
-        if (p->_path == path)
-        {
-            // Found a match
-            p->addRef();
-            return p;
-        }
-    }
-
-    // Open the bundle.
-    Stream* stream = FileSystem::open(path);
-    if (!stream)
-    {
-        GP_ERROR("Failed to open file '%s'.", path);
-        return NULL;
-    }
-
-    // Read the GPB header info.
-    char sig[9];
-    if (stream->read(sig, 1, 9) != 9 || memcmp(sig, "\xABGPB\xBB\r\n\x1A\n", 9) != 0)
-    {
-        SAFE_DELETE(stream);
-        GP_ERROR("Invalid GPB header for bundle '%s'.", path);
-        return NULL;
-    }
-
-    // Read version.
-    unsigned char ver[2];
-    if (stream->read(ver, 1, 2) != 2)
-    {
-        SAFE_DELETE(stream);
-        GP_ERROR("Failed to read GPB version for bundle '%s'.", path);
-        return NULL;
-    }
-    if (ver[0] != BUNDLE_VERSION_MAJOR || ver[1] != BUNDLE_VERSION_MINOR)
-    {
-        SAFE_DELETE(stream);
-        GP_ERROR("Unsupported version (%d.%d) for bundle '%s' (expected %d.%d).", (int)ver[0], (int)ver[1], path, BUNDLE_VERSION_MAJOR, BUNDLE_VERSION_MINOR);
-        return NULL;
-    }
-
-    // Read ref table.
-    unsigned int refCount;
-    if (stream->read(&refCount, 4, 1) != 1)
-    {
-        SAFE_DELETE(stream);
-        GP_ERROR("Failed to read ref table for bundle '%s'.", path);
-        return NULL;
-    }
-
-    // Read all refs.
-    Reference* refs = new Reference[refCount];
-    for (unsigned int i = 0; i < refCount; ++i)
-    {
-        if ((refs[i].id = readString(stream)).empty() ||
-            stream->read(&refs[i].type, 4, 1) != 1 ||
-            stream->read(&refs[i].offset, 4, 1) != 1)
-        {
-            SAFE_DELETE(stream);
-            GP_ERROR("Failed to read ref number %d for bundle '%s'.", i, path);
-            SAFE_DELETE_ARRAY(refs);
-            return NULL;
-        }
-    }
-
-    // Keep file open for faster reading later.
-    Bundle* bundle = new Bundle(path);
-    bundle->_referenceCount = refCount;
-    bundle->_references = refs;
-    bundle->_stream = stream;
-
-	// Add bundle to the cache.
-    __bundleCache.push_back(bundle);
-
-    return bundle;
-}
-
-Bundle::Reference* Bundle::find(const char* id) const
-{
-    GP_ASSERT(id);
-    GP_ASSERT(_references);
-
-    // Search the ref table for the given id (case-sensitive).
-    for (unsigned int i = 0; i < _referenceCount; ++i)
-    {
-        if (_references[i].id == id)
-        {
-            // Found a match
-            return &_references[i];
-        }
-    }
-
-    return NULL;
-}
-
-void Bundle::clearLoadSession()
-{
-    for (size_t i = 0, count = _meshSkins.size(); i < count; ++i)
-    {
-        SAFE_DELETE(_meshSkins[i]);
-    }
-    _meshSkins.clear();
-}
-
-const char* Bundle::getIdFromOffset() const
-{
-    GP_ASSERT(_stream);
-    return getIdFromOffset((unsigned int) _stream->position());
-}
-
-const char* Bundle::getIdFromOffset(unsigned int offset) const
-{
-    // Search the ref table for the given offset.
-    if (offset > 0)
-    {
-        GP_ASSERT(_references);
-        for (unsigned int i = 0; i < _referenceCount; ++i)
-        {
-            if (_references[i].offset == offset && _references[i].id.length() > 0)
-            {
-                return _references[i].id.c_str();
-            }
-        }
-    }
-    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);
-    if (ref == NULL)
-    {
-        GP_ERROR("No object with name '%s' in bundle '%s'.", id, _path.c_str());
-        return NULL;
-    }
-
-    if (ref->type != type)
-    {
-        GP_ERROR("Object '%s' in bundle '%s' has type %d (expected type %d).", id, _path.c_str(), (int)ref->type, (int)type);
-        return NULL;
-    }
-
-    // Seek to the offset of this object.
-    GP_ASSERT(_stream);
-    if (_stream->seek(ref->offset, SEEK_SET) == false)
-    {
-        GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", id, _path.c_str());
-        return NULL;
-    }
-
-    return ref;
-}
-
-Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
-{
-    GP_ASSERT(_references);
-    GP_ASSERT(_stream);
-
-    for (unsigned int i = 0; i < _referenceCount; ++i)
-    {
-        Reference* ref = &_references[i];
-        if (ref->type == type)
-        {
-            // Found a match.
-            if (_stream->seek(ref->offset, SEEK_SET) == false)
-            {
-                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
-                return NULL;
-            }
-            return ref;
-        }
-    }
-    return NULL;
-}
-
-bool Bundle::read(unsigned int* ptr)
-{
-    return _stream->read(ptr, sizeof(unsigned int), 1) == 1;
-}
-
-bool Bundle::read(unsigned char* ptr)
-{
-    return _stream->read(ptr, sizeof(unsigned char), 1) == 1;
-}
-
-bool Bundle::read(float* ptr)
-{
-    return _stream->read(ptr, sizeof(float), 1) == 1;
-}
-
-bool Bundle::readMatrix(float* m)
-{
-    return _stream->read(m, sizeof(float), 16) == 16;
-}
-
-Scene* Bundle::loadScene(const char* id)
-{
-    clearLoadSession();
-
-    Reference* ref = NULL;
-    if (id)
-    {
-        ref = seekTo(id, BUNDLE_TYPE_SCENE);
-        if (!ref)
-        {
-            GP_ERROR("Failed to load scene with id '%s' from bundle.", id);
-            return NULL;
-        }
-    }
-    else
-    {
-        ref = seekToFirstType(BUNDLE_TYPE_SCENE);
-        if (!ref)
-        {
-            GP_ERROR("Failed to load scene from bundle; bundle contains no scene objects.");
-            return NULL;
-        }
-    }
-
-    Scene* scene = Scene::create(getIdFromOffset());
-
-    // Read the number of children.
-    unsigned int childrenCount;
-    if (!read(&childrenCount))
-    {
-        GP_ERROR("Failed to read the scene's number of children.");
-        SAFE_RELEASE(scene);
-        return NULL;
-    }
-    if (childrenCount > 0)
-    {
-        // Read each child directly into the scene.
-        for (unsigned int i = 0; i < childrenCount; i++)
-        {
-            Node* node = readNode(scene, NULL);
-            if (node)
-            {
-                scene->addNode(node);
-                node->release(); // scene now owns node
-            }
-        }
-    }
-    // Read active camera.
-    std::string xref = readString(_stream);
-    if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
-    {
-        Node* node = scene->findNode(xref.c_str() + 1, true);
-        GP_ASSERT(node);
-        Camera* camera = node->getCamera();
-        GP_ASSERT(camera);
-        scene->setActiveCamera(camera);
-    }
-
-    // Read ambient color.
-    float red, blue, green;
-    if (!read(&red))
-    {
-        GP_ERROR("Failed to read red component of the scene's ambient color in bundle '%s'.", _path.c_str());
-        SAFE_RELEASE(scene);
-        return NULL;
-    }
-    if (!read(&green))
-    {
-        GP_ERROR("Failed to read green component of the scene's ambient color in bundle '%s'.", _path.c_str());
-        SAFE_RELEASE(scene);
-        return NULL;
-    }
-    if (!read(&blue))
-    {
-        GP_ERROR("Failed to read blue component of the scene's ambient color in bundle '%s'.", _path.c_str());
-        SAFE_RELEASE(scene);
-        return NULL;
-    }
-    scene->setAmbientColor(red, green, blue);
-
-    // Parse animations.
-    GP_ASSERT(_references);
-    GP_ASSERT(_stream);
-    for (unsigned int i = 0; i < _referenceCount; ++i)
-    {
-        Reference* ref = &_references[i];
-        if (ref->type == BUNDLE_TYPE_ANIMATIONS)
-        {
-            // Found a match.
-            if (_stream->seek(ref->offset, SEEK_SET) == false)
-            {
-                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
-                return NULL;
-            }
-            readAnimations(scene);
-        }
-    }
-
-    resolveJointReferences(scene, NULL);
-
-    return scene;
-}
-
-Node* Bundle::loadNode(const char* id)
-{
-    return loadNode(id, NULL);
-}
-
-Node* Bundle::loadNode(const char* id, Scene* sceneContext)
-{
-    GP_ASSERT(id);
-    GP_ASSERT(_references);
-    GP_ASSERT(_stream);
-
-    clearLoadSession();
-
-    // Load the node and any referenced joints with node tracking enabled.
-    _trackedNodes = new std::map<std::string, Node*>();
-    Node* node = loadNode(id, sceneContext, NULL);
-    if (node)
-        resolveJointReferences(sceneContext, node);
-
-    // Load all animations targeting any nodes or mesh skins under this node's hierarchy.
-    for (unsigned int i = 0; i < _referenceCount; i++)
-    {
-        Reference* ref = &_references[i];
-        if (ref->type == BUNDLE_TYPE_ANIMATIONS)
-        {
-            if (_stream->seek(ref->offset, SEEK_SET) == false)
-            {
-                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
-                SAFE_DELETE(_trackedNodes);
-                return NULL;
-            }
-
-            // Read the number of animations in this object.
-            unsigned int animationCount;
-            if (!read(&animationCount))
-            {
-                GP_ERROR("Failed to read the number of animations for object '%s'.", ref->id.c_str());
-                SAFE_DELETE(_trackedNodes);
-                return NULL;
-            }
-
-            for (unsigned int j = 0; j < animationCount; j++)
-            {
-                const std::string id = readString(_stream);
-
-                // Read the number of animation channels in this animation.
-                unsigned int animationChannelCount;
-                if (!read(&animationChannelCount))
-                {
-                    GP_ERROR("Failed to read the number of animation channels for animation '%s'.", "animationChannelCount", id.c_str());
-                    SAFE_DELETE(_trackedNodes);
-                    return NULL;
-                }
-
-                Animation* animation = NULL;
-                for (unsigned int k = 0; k < animationChannelCount; k++)
-                {
-                    // Read target id.
-                    std::string targetId = readString(_stream);
-                    if (targetId.empty())
-                    {
-                        GP_ERROR("Failed to read target id for animation '%s'.", id.c_str());
-                        SAFE_DELETE(_trackedNodes);
-                        return NULL;
-                    }
-
-                    // If the target is one of the loaded nodes/joints, then load the animation.
-                    std::map<std::string, Node*>::iterator iter = _trackedNodes->find(targetId);
-                    if (iter != _trackedNodes->end())
-                    {
-                        // Read target attribute.
-                        unsigned int targetAttribute;
-                        if (!read(&targetAttribute))
-                        {
-                            GP_ERROR("Failed to read target attribute for animation '%s'.", id.c_str());
-                            SAFE_DELETE(_trackedNodes);
-                            return NULL;
-                        }
-
-                        AnimationTarget* target = iter->second;
-                        if (!target)
-                        {
-                            GP_ERROR("Failed to read %s for %s: %s", "animation target", targetId.c_str(), id.c_str());
-                            SAFE_DELETE(_trackedNodes);
-                            return NULL;
-                        }
-
-                        animation = readAnimationChannelData(animation, id.c_str(), target, targetAttribute);
-                    }
-                    else
-                    {
-                        // Skip over the target attribute.
-                        unsigned int data;
-                        if (!read(&data))
-                        {
-                            GP_ERROR("Failed to skip over target attribute for animation '%s'.", id.c_str());
-                            SAFE_DELETE(_trackedNodes);
-                            return NULL;
-                        }
-
-                        // Skip the animation channel (passing a target attribute of
-                        // 0 causes the animation to not be created).
-                        readAnimationChannelData(NULL, id.c_str(), NULL, 0);
-                    }
-                }
-            }
-        }
-    }
-
-    SAFE_DELETE(_trackedNodes);
-    return node;
-}
-
-Node* Bundle::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
-{
-    GP_ASSERT(id);
-
-    Node* node = NULL;
-
-    // Search the passed in loading contexts (scene/node) first to see
-    // if we've already loaded this node during this load session.
-    if (sceneContext)
-    {
-        node = sceneContext->findNode(id, true);
-        if (node)
-            node->addRef();
-    }
-    if (node == NULL && nodeContext)
-    {
-        node = nodeContext->findNode(id, true);
-        if (node)
-            node->addRef();
-    }
-
-    if (node == NULL)
-    {
-        // If not yet found, search the ref table and read.
-        Reference* ref = seekTo(id, BUNDLE_TYPE_NODE);
-        if (ref == NULL)
-        {
-            return NULL;
-        }
-
-        node = readNode(sceneContext, nodeContext);
-    }
-
-    return node;
-}
-
-bool Bundle::skipNode()
-{
-    const char* id = getIdFromOffset();
-    GP_ASSERT(id);
-    GP_ASSERT(_stream);
-
-    // Skip the node's type.
-    unsigned int nodeType;
-    if (!read(&nodeType))
-    {
-        GP_ERROR("Failed to skip node type for node '%s'.", id);
-        return false;
-    }
-
-    // Skip over the node's transform and parent ID.
-    if (_stream->seek(sizeof(float) * 16, SEEK_CUR) == false)
-    {
-        GP_ERROR("Failed to skip over node transform for node '%s'.", id);
-        return false;
-    }
-    readString(_stream);
-
-    // Skip over the node's children.
-    unsigned int childrenCount;
-    if (!read(&childrenCount))
-    {
-        GP_ERROR("Failed to skip over node's children count for node '%s'.", id);
-        return false;
-    }
-    else if (childrenCount > 0)
-    {
-        for (unsigned int i = 0; i < childrenCount; i++)
-        {
-            if (!skipNode())
-                return false;
-        }
-    }
-
-    // Skip over the node's camera, light, and model attachments.
-    Camera* camera = readCamera(); SAFE_RELEASE(camera);
-    Light* light = readLight(); SAFE_RELEASE(light);
-    Model* model = readModel(id); SAFE_RELEASE(model);
-
-    return true;
-}
-
-Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
-{
-    const char* id = getIdFromOffset();
-    GP_ASSERT(id);
-    GP_ASSERT(_stream);
-
-    // If we are tracking nodes and it's not in the set yet, add it.
-    if (_trackedNodes)
-    {
-        std::map<std::string, Node*>::iterator iter = _trackedNodes->find(id);
-        if (iter != _trackedNodes->end())
-        {
-            // Skip over this node since we previously read it
-            if (!skipNode())
-                return NULL;
-
-            iter->second->addRef();
-            return iter->second;
-        }
-    }
-
-    // Read node type.
-    unsigned int nodeType;
-    if (!read(&nodeType))
-    {
-        GP_ERROR("Failed to read node type for node '%s'.", id);
-        return NULL;
-    }
-
-    Node* node = NULL;
-    switch (nodeType)
-    {
-    case Node::NODE:
-        node = Node::create(id);
-        break;
-    case Node::JOINT:
-        node = Joint::create(id);
-        break;
-    default:
-        return NULL;
-    }
-
-    if (_trackedNodes)
-    {
-        // Add the new node to the list of tracked nodes
-        _trackedNodes->insert(std::make_pair(id, node));
-    }
-
-    // If no loading context is set, set this node as the loading context.
-    if (sceneContext == NULL && nodeContext == NULL)
-    {
-        nodeContext = node;
-    }
-
-    // Read transform.
-    float transform[16];
-    if (_stream->read(transform, sizeof(float), 16) != 16)
-    {
-        GP_ERROR("Failed to read transform for node '%s'.", id);
-        SAFE_RELEASE(node);
-        return NULL;
-    }
-    setTransform(transform, node);
-
-    // Skip the parent ID.
-    readString(_stream);
-
-    // Read children.
-    unsigned int childrenCount;
-    if (!read(&childrenCount))
-    {
-        GP_ERROR("Failed to read children count for node '%s'.", id);
-        SAFE_RELEASE(node);
-        return NULL;
-    }
-    if (childrenCount > 0)
-    {
-        // Read each child.
-        for (unsigned int i = 0; i < childrenCount; i++)
-        {
-            // Search the passed in loading contexts (scene/node) first to see
-            // if we've already loaded this child node during this load session.
-            Node* child = NULL;
-            id = getIdFromOffset();
-            GP_ASSERT(id);
-
-            if (sceneContext)
-            {
-                child = sceneContext->findNode(id, true);
-            }
-            if (child == NULL && nodeContext)
-            {
-                child = nodeContext->findNode(id, true);
-            }
-
-            // If the child was already loaded, skip it, otherwise read it
-            if (child)
-            {
-                skipNode();
-            }
-            else
-            {
-                child = readNode(sceneContext, nodeContext);
-            }
-
-            if (child)
-            {
-                node->addChild(child);
-                child->release(); // 'node' now owns this child
-            }
-        }
-    }
-
-    // Read camera.
-    Camera* camera = readCamera();
-    if (camera)
-    {
-        node->setCamera(camera);
-        SAFE_RELEASE(camera);
-    }
-
-    // Read light.
-    Light* light = readLight();
-    if (light)
-    {
-        node->setLight(light);
-        SAFE_RELEASE(light);
-    }
-
-    // Read model.
-    Model* model = readModel(node->getId());
-    if (model)
-    {
-        node->setModel(model);
-        SAFE_RELEASE(model);
-    }
-
-    return node;
-}
-
-Camera* Bundle::readCamera()
-{
-    unsigned char cameraType;
-    if (!read(&cameraType))
-    {
-        GP_ERROR("Failed to load camera type in bundle '%s'.", _path.c_str());
-        return NULL;
-    }
-
-    // Check if there isn't a camera to load.
-    if (cameraType == 0)
-    {
-        return NULL;
-    }
-
-    float aspectRatio;
-    if (!read(&aspectRatio))
-    {
-        GP_ERROR("Failed to load camera aspect ratio in bundle '%s'.", _path.c_str());
-        return NULL;
-    }
-
-    float nearPlane;
-    if (!read(&nearPlane))
-    {
-        GP_ERROR("Failed to load camera near plane in bundle '%s'.", _path.c_str());
-        return NULL;
-    }
-
-    float farPlane;
-    if (!read(&farPlane))
-    {
-        GP_ERROR("Failed to load camera far plane in bundle '%s'.", _path.c_str());
-        return NULL;
-    }
-
-    Camera* camera = NULL;
-    if (cameraType == Camera::PERSPECTIVE)
-    {
-        float fieldOfView;
-        if (!read(&fieldOfView))
-        {
-            GP_ERROR("Failed to load camera field of view in bundle '%s'.", _path.c_str());
-            return NULL;
-        }
-
-        camera = Camera::createPerspective(fieldOfView, aspectRatio, nearPlane, farPlane);
-    }
-    else if (cameraType == Camera::ORTHOGRAPHIC)
-    {
-        float zoomX;
-        if (!read(&zoomX))
-        {
-            GP_ERROR("Failed to load camera zoomX in bundle '%s'.", _path.c_str());
-            return NULL;
-        }
-
-        float zoomY;
-        if (!read(&zoomY))
-        {
-            GP_ERROR("Failed to load camera zoomY in bundle '%s'.", _path.c_str());
-            return NULL;
-        }
-
-        camera = Camera::createOrthographic(zoomX, zoomY, aspectRatio, nearPlane, farPlane);
-    }
-    else
-    {
-        GP_ERROR("Unsupported camera type (%d) in bundle '%s'.", cameraType, _path.c_str());
-        return NULL;
-    }
-    return camera;
-}
-
-Light* Bundle::readLight()
-{
-    unsigned char type;
-    if (!read(&type))
-    {
-        GP_ERROR("Failed to load light type in bundle '%s'.", _path.c_str());
-        return NULL;
-    }
-
-    // Check if there isn't a light to load.
-    if (type == 0)
-    {
-        return NULL;
-    }
-
-    // Read color.
-    float red, blue, green;
-    if (!read(&red) || !read(&blue) || !read(&green))
-    {
-        GP_ERROR("Failed to load light color in bundle '%s'.", _path.c_str());
-        return NULL;
-    }
-    Vector3 color(red, blue, green);
-
-    Light* light = NULL;
-    if (type == Light::DIRECTIONAL)
-    {
-        light = Light::createDirectional(color);
-    }
-    else if (type == Light::POINT)
-    {
-        float range;
-        if (!read(&range))
-        {
-            GP_ERROR("Failed to load point light range in bundle '%s'.", _path.c_str());
-            return NULL;
-        }
-        light = Light::createPoint(color, range);
-    }
-    else if (type == Light::SPOT)
-    {
-        float range, innerAngle, outerAngle;
-        if (!read(&range))
-        {
-            GP_ERROR("Failed to load spot light range in bundle '%s'.", _path.c_str());
-            return NULL;
-        }
-        if (!read(&innerAngle))
-        {
-            GP_ERROR("Failed to load spot light inner angle in bundle '%s'.", _path.c_str());
-            return NULL;
-        }
-        if (!read(&outerAngle))
-        {
-            GP_ERROR("Failed to load spot light outer angle in bundle '%s'.", _path.c_str());
-            return NULL;
-        }
-        light = Light::createSpot(color, range, innerAngle, outerAngle);
-    }
-    else
-    {
-        GP_ERROR("Unsupported light type (%d) in bundle '%s'.", type, _path.c_str());
-        return NULL;
-    }
-    return light;
-}
-
-Model* Bundle::readModel(const char* nodeId)
-{
-    std::string xref = readString(_stream);
-    if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
-    {
-        Mesh* mesh = loadMesh(xref.c_str() + 1, nodeId);
-        if (mesh)
-        {
-            Model* model = Model::create(mesh);
-            SAFE_RELEASE(mesh);
-
-            // Read skin.
-            unsigned char hasSkin;
-            if (!read(&hasSkin))
-            {
-                GP_ERROR("Failed to load whether model with mesh '%s' has a mesh skin in bundle '%s'.", xref.c_str() + 1, _path.c_str());
-                return NULL;
-            }
-            if (hasSkin)
-            {
-                MeshSkin* skin = readMeshSkin();
-                if (skin)
-                {
-                    model->setSkin(skin);
-                }
-            }
-            // Read material.
-            unsigned int materialCount;
-            if (!read(&materialCount))
-            {
-                GP_ERROR("Failed to load material count for model with mesh '%s' in bundle '%s'.", xref.c_str() + 1, _path.c_str());
-                return NULL;
-            }
-            if (materialCount > 0)
-            {
-                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;
-        }
-    }
-
-    return NULL;
-}
-
-MeshSkin* Bundle::readMeshSkin()
-{
-    MeshSkin* meshSkin = new MeshSkin();
-
-    // Read bindShape.
-    float bindShape[16];
-    if (!readMatrix(bindShape))
-    {
-        GP_ERROR("Failed to load bind shape for mesh skin in bundle '%s'.", _path.c_str());
-        SAFE_DELETE(meshSkin);
-        return NULL;
-    }
-    meshSkin->setBindShape(bindShape);
-
-    MeshSkinData* skinData = new MeshSkinData();
-    skinData->skin = meshSkin;
-
-    // Read joint count.
-    unsigned int jointCount;
-    if (!read(&jointCount))
-    {
-        GP_ERROR("Failed to load joint count for mesh skin in bundle '%s'.", _path.c_str());
-        SAFE_DELETE(meshSkin);
-        SAFE_DELETE(skinData);
-        return NULL;
-    }
-    if (jointCount == 0)
-    {
-        GP_ERROR("Invalid joint count (must be greater than 0) for mesh skin in bundle '%s'.", _path.c_str());
-        SAFE_DELETE(meshSkin);
-        SAFE_DELETE(skinData);
-        return NULL;
-    }
-    meshSkin->setJointCount(jointCount);
-
-    // Read joint xref strings for all joints in the list.
-    for (unsigned int i = 0; i < jointCount; i++)
-    {
-        skinData->joints.push_back(readString(_stream));
-    }
-
-    // Read bind poses.
-    unsigned int jointsBindPosesCount;
-    if (!read(&jointsBindPosesCount))
-    {
-        GP_ERROR("Failed to load number of joint bind poses in bundle '%s'.", _path.c_str());
-        SAFE_DELETE(meshSkin);
-        SAFE_DELETE(skinData);
-        return NULL;
-    }
-    if (jointsBindPosesCount > 0)
-    {
-        GP_ASSERT(jointCount * 16 == jointsBindPosesCount);
-        float m[16];
-        for (unsigned int i = 0; i < jointCount; i++)
-        {
-            if (!readMatrix(m))
-            {
-                GP_ERROR("Failed to load joint bind pose matrix (for joint with index %d) in bundle '%s'.", i, _path.c_str());
-                SAFE_DELETE(meshSkin);
-                SAFE_DELETE(skinData);
-                return NULL;
-            }
-            skinData->inverseBindPoseMatrices.push_back(m);
-        }
-    }
-
-    // Store the MeshSkinData so we can go back and resolve all joint references later.
-    _meshSkins.push_back(skinData);
-
-    return meshSkin;
-}
-
-void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
-{
-    GP_ASSERT(_stream);
-
-    for (size_t i = 0, skinCount = _meshSkins.size(); i < skinCount; ++i)
-    {
-        MeshSkinData* skinData = _meshSkins[i];
-        GP_ASSERT(skinData);
-        GP_ASSERT(skinData->skin);
-
-        // Resolve all joints in skin joint list.
-        size_t jointCount = skinData->joints.size();
-        for (size_t j = 0; j < jointCount; ++j)
-        {
-            // TODO: Handle full xrefs (not just local # xrefs).
-            std::string jointId = skinData->joints[j];
-            if (jointId.length() > 1 && jointId[0] == '#')
-            {
-                jointId = jointId.substr(1, jointId.length() - 1);
-
-                Node* n = loadNode(jointId.c_str(), sceneContext, nodeContext);
-                if (n && n->getType() == Node::JOINT)
-                {
-                    Joint* joint = static_cast<Joint*>(n);
-                    joint->setInverseBindPose(skinData->inverseBindPoseMatrices[j]);
-                    skinData->skin->setJoint(joint, (unsigned int)j);
-                    SAFE_RELEASE(joint);
-                }
-            }
-        }
-
-        // Set the root joint.
-        if (jointCount > 0)
-        {
-            Joint* rootJoint = skinData->skin->getJoint((unsigned int)0);
-            Node* node = rootJoint;
-            GP_ASSERT(node);
-            Node* parent = node->getParent();
-
-            std::vector<Node*> loadedNodes;
-            while (true)
-            {
-                if (parent)
-                {
-                    if (skinData->skin->getJointIndex(static_cast<Joint*>(parent)) != -1)
-                    {
-                        // Parent is a joint in the MeshSkin, so treat it as the new root.
-                        rootJoint = static_cast<Joint*>(parent);
-                    }
-
-                    node = parent;
-                    parent = node->getParent();
-                }
-                else
-                {
-                    // No parent currently set for this joint.
-                    // Lookup its parentID in case it references a node that was not yet loaded as part
-                    // of the mesh skin's joint list.
-                    std::string nodeId = node->getId();
-
-                    while (true)
-                    {
-                        // Get the node's type.
-                        Reference* ref = find(nodeId.c_str());
-                        if (ref == NULL)
-                        {
-                            GP_ERROR("No object with name '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
-                            return;
-                        }
-
-                        // Seek to the current node in the file so we can get it's parent ID.
-                        seekTo(nodeId.c_str(), ref->type);
-
-                        // Skip over the node type (1 unsigned int) and transform (16 floats) and read the parent id.
-                        if (_stream->seek(sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR) == false)
-                        {
-                            GP_ERROR("Failed to skip over node type and transform for node '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
-                            return;
-                        }
-                        std::string parentID = readString(_stream);
-
-                        if (!parentID.empty())
-                            nodeId = parentID;
-                        else
-                            break;
-                    }
-
-                    if (nodeId != rootJoint->getId())
-                        loadedNodes.push_back(loadNode(nodeId.c_str(), sceneContext, nodeContext));
-
-                    break;
-                }
-            }
-
-            skinData->skin->setRootJoint(rootJoint);
-
-            // Release all the nodes that we loaded since the nodes are now owned by the mesh skin/joints.
-            for (unsigned int i = 0; i < loadedNodes.size(); i++)
-            {
-                SAFE_RELEASE(loadedNodes[i]);
-            }
-        }
-
-        // Remove the joint hierarchy from the scene since it is owned by the mesh skin.
-        if (sceneContext)
-            sceneContext->removeNode(skinData->skin->_rootNode);
-
-        // Done with this MeshSkinData entry.
-        SAFE_DELETE(_meshSkins[i]);
-    }
-    _meshSkins.clear();
-}
-
-void Bundle::readAnimation(Scene* scene)
-{
-    const std::string animationId = readString(_stream);
-
-    // Read the number of animation channels in this animation.
-    unsigned int animationChannelCount;
-    if (!read(&animationChannelCount))
-    {
-        GP_ERROR("Failed to read animation channel count for animation '%s'.", animationId.c_str());
-        return;
-    }
-
-    Animation* animation = NULL;
-    for (unsigned int i = 0; i < animationChannelCount; i++)
-    {
-        animation = readAnimationChannel(scene, animation, animationId.c_str());
-    }
-}
-
-void Bundle::readAnimations(Scene* scene)
-{
-    // Read the number of animations in this object.
-    unsigned int animationCount;
-    if (!read(&animationCount))
-    {
-        GP_ERROR("Failed to read the number of animations in the scene.");
-        return;
-    }
-
-    for (unsigned int i = 0; i < animationCount; i++)
-    {
-        readAnimation(scene);
-    }
-}
-
-Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, const char* animationId)
-{
-    GP_ASSERT(animationId);
-
-    // Read target id.
-    std::string targetId = readString(_stream);
-    if (targetId.empty())
-    {
-        GP_ERROR("Failed to read target id for animation '%s'.", animationId);
-        return NULL;
-    }
-
-    // Read target attribute.
-    unsigned int targetAttribute;
-    if (!read(&targetAttribute))
-    {
-        GP_ERROR("Failed to read target attribute for animation '%s'.", animationId);
-        return NULL;
-    }
-
-    AnimationTarget* target = NULL;
-
-    // Search for a node that matches the target.
-    if (!target)
-    {
-        target = scene->findNode(targetId.c_str());
-        if (!target)
-        {
-            GP_ERROR("Failed to find the animation target (with id '%s') for animation '%s'.", targetId.c_str(), animationId);
-            return NULL;
-        }
-    }
-
-    return readAnimationChannelData(animation, animationId, target, targetAttribute);
-}
-
-Animation* Bundle::readAnimationChannelData(Animation* animation, const char* id, AnimationTarget* target, unsigned int targetAttribute)
-{
-    GP_ASSERT(id);
-
-    std::vector<unsigned int> keyTimes;
-    std::vector<float> values;
-    std::vector<float> tangentsIn;
-    std::vector<float> tangentsOut;
-    std::vector<unsigned int> interpolation;
-
-    // Length of the arrays.
-    unsigned int keyTimesCount;
-    unsigned int valuesCount;
-    unsigned int tangentsInCount;
-    unsigned int tangentsOutCount;
-    unsigned int interpolationCount;
-
-    // Read key times.
-    if (!readArray(&keyTimesCount, &keyTimes, sizeof(unsigned int)))
-    {
-        GP_ERROR("Failed to read key times for animation '%s'.", id);
-        return NULL;
-    }
-
-    // Read key values.
-    if (!readArray(&valuesCount, &values))
-    {
-        GP_ERROR("Failed to read key values for animation '%s'.", id);
-        return NULL;
-    }
-
-    // Read in-tangents.
-    if (!readArray(&tangentsInCount, &tangentsIn))
-    {
-        GP_ERROR("Failed to read in tangents for animation '%s'.", id);
-        return NULL;
-    }
-
-    // Read out-tangents.
-    if (!readArray(&tangentsOutCount, &tangentsOut))
-    {
-        GP_ERROR("Failed to read out tangents for animation '%s'.", id);
-        return NULL;
-    }
-
-    // Read interpolations.
-    if (!readArray(&interpolationCount, &interpolation, sizeof(unsigned int)))
-    {
-        GP_ERROR("Failed to read the interpolation values for animation '%s'.", id);
-        return NULL;
-    }
-
-    if (targetAttribute > 0)
-    {
-        GP_ASSERT(target);
-        GP_ASSERT(keyTimes.size() > 0 && values.size() > 0);
-        if (animation == NULL)
-        {
-            // TODO: This code currently assumes LINEAR only.
-            animation = target->createAnimation(id, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
-        }
-        else
-        {
-            animation->createChannel(target, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
-        }
-    }
-
-    return animation;
-}
-
-Mesh* Bundle::loadMesh(const char* id)
-{
-    return loadMesh(id, NULL);
-}
-
-Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
-{
-    GP_ASSERT(_stream);
-    GP_ASSERT(id);
-
-    // Save the file position.
-    long position = _stream->position();
-    if (position == -1L)
-    {
-        GP_ERROR("Failed to save the current file position before loading mesh '%s'.", id);
-        return NULL;
-    }
-
-    // Seek to the specified mesh.
-    Reference* ref = seekTo(id, BUNDLE_TYPE_MESH);
-    if (ref == NULL)
-    {
-        GP_ERROR("Failed to locate ref for mesh '%s'.", id);
-        return NULL;
-    }
-
-    // Read mesh data.
-    MeshData* meshData = readMeshData();
-    if (meshData == NULL)
-    {
-        GP_ERROR("Failed to load mesh data for mesh '%s'.", id);
-        return NULL;
-    }
-
-    // Create mesh.
-    Mesh* mesh = Mesh::createMesh(meshData->vertexFormat, meshData->vertexCount, false);
-    if (mesh == NULL)
-    {
-        GP_ERROR("Failed to create mesh '%s'.", id);
-        SAFE_DELETE_ARRAY(meshData);
-        return NULL;
-    }
-
-    mesh->_url = _path;
-    mesh->_url += "#";
-    mesh->_url += id;
-
-    mesh->setVertexData((float*)meshData->vertexData, 0, meshData->vertexCount);
-
-    mesh->_boundingBox.set(meshData->boundingBox);
-    mesh->_boundingSphere.set(meshData->boundingSphere);
-
-    // Create mesh parts.
-    for (unsigned int i = 0; i < meshData->parts.size(); ++i)
-    {
-        MeshPartData* partData = meshData->parts[i];
-        GP_ASSERT(partData);
-
-        MeshPart* part = mesh->addPart(partData->primitiveType, partData->indexFormat, partData->indexCount, false);
-        if (part == NULL)
-        {
-            GP_ERROR("Failed to create mesh part (with index %d) for mesh '%s'.", i, id);
-            SAFE_DELETE(meshData);
-            return NULL;
-        }
-        part->setIndexData(partData->indexData, 0, partData->indexCount);
-    }
-
-    SAFE_DELETE(meshData);
-
-    // Restore file pointer.
-    if (_stream->seek(position, SEEK_SET) == false)
-    {
-        GP_ERROR("Failed to restore file pointer after loading mesh '%s'.", id);
-        return NULL;
-    }
-
-    return mesh;
-}
-
-Bundle::MeshData* Bundle::readMeshData()
-{
-    // Read vertex format/elements.
-    unsigned int vertexElementCount;
-    if (_stream->read(&vertexElementCount, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to load vertex element count.");
-        return NULL;
-    }
-    if (vertexElementCount < 1)
-    {
-        GP_ERROR("Failed to load mesh data; invalid vertex element count (must be greater than 0).");
-        return NULL;
-    }
-
-    VertexFormat::Element* vertexElements = new VertexFormat::Element[vertexElementCount];
-    for (unsigned int i = 0; i < vertexElementCount; ++i)
-    {
-        unsigned int vUsage, vSize;
-        if (_stream->read(&vUsage, 4, 1) != 1)
-        {
-            GP_ERROR("Failed to load vertex usage.");
-            SAFE_DELETE_ARRAY(vertexElements);
-            return NULL;
-        }
-        if (_stream->read(&vSize, 4, 1) != 1)
-        {
-            GP_ERROR("Failed to load vertex size.");
-            SAFE_DELETE_ARRAY(vertexElements);
-            return NULL;
-        }
-
-        vertexElements[i].usage = (VertexFormat::Usage)vUsage;
-        vertexElements[i].size = vSize;
-    }
-
-    MeshData* meshData = new MeshData(VertexFormat(vertexElements, vertexElementCount));
-    SAFE_DELETE_ARRAY(vertexElements);
-
-    // Read vertex data.
-    unsigned int vertexByteCount;
-    if (_stream->read(&vertexByteCount, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to load vertex byte count.");
-        SAFE_DELETE(meshData);
-        return NULL;
-    }
-    if (vertexByteCount == 0)
-    {
-        GP_ERROR("Failed to load mesh data; invalid vertex byte count of 0.");
-        SAFE_DELETE(meshData);
-        return NULL;
-    }
-
-    GP_ASSERT(meshData->vertexFormat.getVertexSize());
-    meshData->vertexCount = vertexByteCount / meshData->vertexFormat.getVertexSize();
-    meshData->vertexData = new unsigned char[vertexByteCount];
-    if (_stream->read(meshData->vertexData, 1, vertexByteCount) != vertexByteCount)
-    {
-        GP_ERROR("Failed to load vertex data.");
-        SAFE_DELETE(meshData);
-        return NULL;
-    }
-
-    // Read mesh bounds (bounding box and bounding sphere).
-    if (_stream->read(&meshData->boundingBox.min.x, 4, 3) != 3 || _stream->read(&meshData->boundingBox.max.x, 4, 3) != 3)
-    {
-        GP_ERROR("Failed to load mesh bounding box.");
-        SAFE_DELETE(meshData);
-        return NULL;
-    }
-    if (_stream->read(&meshData->boundingSphere.center.x, 4, 3) != 3 || _stream->read(&meshData->boundingSphere.radius, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to load mesh bounding sphere.");
-        SAFE_DELETE(meshData);
-        return NULL;
-    }
-
-    // Read mesh parts.
-    unsigned int meshPartCount;
-    if (_stream->read(&meshPartCount, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to load mesh part count.");
-        SAFE_DELETE(meshData);
-        return NULL;
-    }
-    for (unsigned int i = 0; i < meshPartCount; ++i)
-    {
-        // Read primitive type, index format and index count.
-        unsigned int pType, iFormat, iByteCount;
-        if (_stream->read(&pType, 4, 1) != 1)
-        {
-            GP_ERROR("Failed to load primitive type for mesh part with index %d.", i);
-            SAFE_DELETE(meshData);
-            return NULL;
-        }
-        if (_stream->read(&iFormat, 4, 1) != 1)
-        {
-            GP_ERROR("Failed to load index format for mesh part with index %d.", i);
-            SAFE_DELETE(meshData);
-            return NULL;
-        }
-        if (_stream->read(&iByteCount, 4, 1) != 1)
-        {
-            GP_ERROR("Failed to load index byte count for mesh part with index %d.", i);
-            SAFE_DELETE(meshData);
-            return NULL;
-        }
-
-        MeshPartData* partData = new MeshPartData();
-        meshData->parts.push_back(partData);
-
-        partData->primitiveType = (Mesh::PrimitiveType)pType;
-        partData->indexFormat = (Mesh::IndexFormat)iFormat;
-
-        unsigned int indexSize = 0;
-        switch (partData->indexFormat)
-        {
-        case Mesh::INDEX8:
-            indexSize = 1;
-            break;
-        case Mesh::INDEX16:
-            indexSize = 2;
-            break;
-        case Mesh::INDEX32:
-            indexSize = 4;
-            break;
-        default:
-            GP_ERROR("Unsupported index format for mesh part with index %d.", i);
-            return NULL;
-        }
-
-        GP_ASSERT(indexSize);
-        partData->indexCount = iByteCount / indexSize;
-
-        partData->indexData = new unsigned char[iByteCount];
-        if (_stream->read(partData->indexData, 1, iByteCount) != iByteCount)
-        {
-            GP_ERROR("Failed to read index data for mesh part with index %d.", i);
-            SAFE_DELETE(meshData);
-            return NULL;
-        }
-    }
-
-    return meshData;
-}
-
-Bundle::MeshData* Bundle::readMeshData(const char* url)
-{
-    GP_ASSERT(url);
-
-    size_t len = strlen(url);
-    if (len == 0)
-    {
-        GP_ERROR("Mesh data URL must be non-empty.");
-        return NULL;
-    }
-
-    // Parse URL (formatted as 'bundle#id').
-    std::string urlstring(url);
-    size_t pos = urlstring.find('#');
-    if (pos == std::string::npos)
-    {
-        GP_ERROR("Invalid mesh data URL '%s' (must be of the form 'bundle#id').", url);
-        return NULL;
-    }
-
-    std::string file = urlstring.substr(0, pos);
-    std::string id = urlstring.substr(pos + 1);
-
-    // Load bundle.
-    Bundle* bundle = Bundle::create(file.c_str());
-    if (bundle == NULL)
-    {
-        GP_ERROR("Failed to load bundle '%s'.", file.c_str());
-        return NULL;
-    }
-
-    // Seek to mesh with specified ID in bundle.
-    Reference* ref = bundle->seekTo(id.c_str(), BUNDLE_TYPE_MESH);
-    if (ref == NULL)
-    {
-        GP_ERROR("Failed to load ref from bundle '%s' for mesh with id '%s'.", file.c_str(), id.c_str());
-        return NULL;
-    }
-
-    // Read mesh data from current file position.
-    MeshData* meshData = bundle->readMeshData();
-
-    SAFE_RELEASE(bundle);
-
-    return meshData;
-}
-
-Font* Bundle::loadFont(const char* id)
-{
-    GP_ASSERT(id);
-    GP_ASSERT(_stream);
-
-    // Seek to the specified font.
-    Reference* ref = seekTo(id, BUNDLE_TYPE_FONT);
-    if (ref == NULL)
-    {
-        GP_ERROR("Failed to load ref for font '%s'.", id);
-        return NULL;
-    }
-
-    // Read font family.
-    std::string family = readString(_stream);
-    if (family.empty())
-    {
-        GP_ERROR("Failed to read font family for font '%s'.", id);
-        return NULL;
-    }
-
-    // Read font style and size.
-    unsigned int style, size;
-    if (_stream->read(&style, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to read style for font '%s'.", id);
-        return NULL;
-    }
-    if (_stream->read(&size, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to read size for font '%s'.", id);
-        return NULL;
-    }
-
-    // Read character set.
-    std::string charset = readString(_stream);
-
-    // Read font glyphs.
-    unsigned int glyphCount;
-    if (_stream->read(&glyphCount, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to read glyph count for font '%s'.", id);
-        return NULL;
-    }
-    if (glyphCount == 0)
-    {
-        GP_ERROR("Invalid glyph count (must be greater than 0) for font '%s'.", id);
-        return NULL;
-    }
-
-    Font::Glyph* glyphs = new Font::Glyph[glyphCount];
-    if (_stream->read(glyphs, sizeof(Font::Glyph), glyphCount) != glyphCount)
-    {
-        GP_ERROR("Failed to read glyphs for font '%s'.", id);
-        SAFE_DELETE_ARRAY(glyphs);
-        return NULL;
-    }
-
-    // Read texture attributes.
-    unsigned int width, height, textureByteCount;
-    if (_stream->read(&width, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to read texture width for font '%s'.", id);
-        SAFE_DELETE_ARRAY(glyphs);
-        return NULL;
-    }
-    if (_stream->read(&height, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to read texture height for font '%s'.", id);
-        SAFE_DELETE_ARRAY(glyphs);
-        return NULL;
-    }
-    if (_stream->read(&textureByteCount, 4, 1) != 1)
-    {
-        GP_ERROR("Failed to read texture byte count for font '%s'.", id);
-        SAFE_DELETE_ARRAY(glyphs);
-        return NULL;
-    }
-    if (textureByteCount != (width * height))
-    {
-        GP_ERROR("Invalid texture byte count for font '%s'.", id);
-        SAFE_DELETE_ARRAY(glyphs);
-        return NULL;
-    }
-
-    // Read texture data.
-    unsigned char* textureData = new unsigned char[textureByteCount];
-    if (_stream->read(textureData, 1, textureByteCount) != textureByteCount)
-    {
-        GP_ERROR("Failed to read texture data for font '%s'.", id);
-        SAFE_DELETE_ARRAY(glyphs);
-        SAFE_DELETE_ARRAY(textureData);
-        return NULL;
-    }
-
-    // Create the texture for the font.
-    Texture* texture = Texture::create(Texture::ALPHA, width, height, textureData, true);
-
-    // Free the texture data (no longer needed).
-    SAFE_DELETE_ARRAY(textureData);
-
-    if (texture == NULL)
-    {
-        GP_ERROR("Failed to create texture for font '%s'.", id);
-        SAFE_DELETE_ARRAY(glyphs);
-        return NULL;
-    }
-
-    // Create the font.
-    Font* font = Font::create(family.c_str(), Font::PLAIN, size, glyphs, glyphCount, texture);
-
-    // Free the glyph array.
-    SAFE_DELETE_ARRAY(glyphs);
-
-    // Release the texture since the Font now owns it.
-    SAFE_RELEASE(texture);
-
-    if (font)
-    {
-        font->_path = _path;
-        font->_id = id;
-    }
-
-    return font;
-}
-
-void Bundle::setTransform(const float* values, Transform* transform)
-{
-    GP_ASSERT(transform);
-
-    // Load array into transform.
-    Matrix matrix(values);
-    Vector3 scale, translation;
-    Quaternion rotation;
-    matrix.decompose(&scale, &rotation, &translation);
-    transform->setScale(scale);
-    transform->setTranslation(translation);
-    transform->setRotation(rotation);
-}
-
-bool Bundle::contains(const char* id) const
-{
-    return (find(id) != NULL);
-}
-
-unsigned int Bundle::getObjectCount() const
-{
-    return _referenceCount;
-}
-
-const char* Bundle::getObjectId(unsigned int index) const
-{
-    GP_ASSERT(_references);
-    return (index >= _referenceCount ? NULL : _references[index].id.c_str());
-}
-
-Bundle::Reference::Reference()
-    : type(0), offset(0)
-{
-}
-
-Bundle::Reference::~Reference()
-{
-}
-
-Bundle::MeshPartData::MeshPartData() :
-    indexCount(0), indexData(NULL)
-{
-}
-
-Bundle::MeshPartData::~MeshPartData()
-{
-    SAFE_DELETE_ARRAY(indexData);
-}
-
-Bundle::MeshData::MeshData(const VertexFormat& vertexFormat)
-    : vertexFormat(vertexFormat), vertexCount(0), vertexData(NULL)
-{
-}
-
-Bundle::MeshData::~MeshData()
-{
-    SAFE_DELETE_ARRAY(vertexData);
-
-    for (unsigned int i = 0; i < parts.size(); ++i)
-    {
-        SAFE_DELETE(parts[i]);
-    }
-}
-
-}
+#include "Base.h"
+#include "Bundle.h"
+#include "FileSystem.h"
+#include "MeshPart.h"
+#include "Scene.h"
+#include "Joint.h"
+
+// Minimum version numbers supported
+#define BUNDLE_VERSION_MAJOR_REQUIRED   1 
+#define BUNDLE_VERSION_MINOR_REQUIRED   2
+
+#define BUNDLE_TYPE_SCENE               1
+#define BUNDLE_TYPE_NODE                2
+#define BUNDLE_TYPE_ANIMATIONS          3
+#define BUNDLE_TYPE_ANIMATION           4
+#define BUNDLE_TYPE_ANIMATION_CHANNEL   5
+#define BUNDLE_TYPE_MODEL               10
+#define BUNDLE_TYPE_MATERIAL            16
+#define BUNDLE_TYPE_EFFECT              18
+#define BUNDLE_TYPE_CAMERA              32
+#define BUNDLE_TYPE_LIGHT               33
+#define BUNDLE_TYPE_MESH                34
+#define BUNDLE_TYPE_MESHPART            35
+#define BUNDLE_TYPE_MESHSKIN            36
+#define BUNDLE_TYPE_FONT                128
+
+// For sanity checking string reads
+#define BUNDLE_MAX_STRING_LENGTH        5000
+
+#define BUNDLE_VERSION_MAJOR_FONT_FORMAT  1
+#define BUNDLE_VERSION_MINOR_FONT_FORMAT  4
+
+namespace gameplay
+{
+
+static std::vector<Bundle*> __bundleCache;
+
+Bundle::Bundle(const char* path) :
+    _path(path), _referenceCount(0), _references(NULL), _stream(NULL), _trackedNodes(NULL)
+{
+}
+
+Bundle::~Bundle()
+{
+    clearLoadSession();
+
+    // Remove this Bundle from the cache.
+    std::vector<Bundle*>::iterator itr = std::find(__bundleCache.begin(), __bundleCache.end(), this);
+    if (itr != __bundleCache.end())
+    {
+        __bundleCache.erase(itr);
+    }
+
+    SAFE_DELETE_ARRAY(_references);
+
+    if (_stream)
+    {
+        SAFE_DELETE(_stream);
+    }
+}
+
+unsigned int Bundle::getVersionMajor() const
+{
+    return (unsigned int)_version[0];
+}
+
+unsigned int Bundle::getVersionMinor() const
+{
+    return (unsigned int)_version[1];
+}
+
+template <class T>
+bool Bundle::readArray(unsigned int* length, T** ptr)
+{
+    GP_ASSERT(length);
+    GP_ASSERT(ptr);
+    GP_ASSERT(_stream);
+
+    if (!read(length))
+    {
+        GP_ERROR("Failed to read the length of an array of data (to be read into an array).");
+        return false;
+    }
+    if (*length > 0)
+    {
+        *ptr = new T[*length];
+        if (_stream->read(*ptr, sizeof(T), *length) != *length)
+        {
+            GP_ERROR("Failed to read an array of data from bundle (into an array).");
+            SAFE_DELETE_ARRAY(*ptr);
+            return false;
+        }
+    }
+    return true;
+}
+
+template <class T>
+bool Bundle::readArray(unsigned int* length, std::vector<T>* values)
+{
+    GP_ASSERT(length);
+    GP_ASSERT(_stream);
+
+    if (!read(length))
+    {
+        GP_ERROR("Failed to read the length of an array of data (to be read into a std::vector).");
+        return false;
+    }
+    if (*length > 0 && values)
+    {
+        values->resize(*length);
+        if (_stream->read(&(*values)[0], sizeof(T), *length) != *length)
+        {
+            GP_ERROR("Failed to read an array of data from bundle (into a std::vector).");
+            return false;
+        }
+    }
+    return true;
+}
+
+template <class T>
+bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize)
+{
+    GP_ASSERT(length);
+    GP_ASSERT(_stream);
+    GP_ASSERT(sizeof(T) >= readSize);
+
+    if (!read(length))
+    {
+        GP_ERROR("Failed to read the length of an array of data (to be read into a std::vector with a specified single element read size).");
+        return false;
+    }
+    if (*length > 0 && values)
+    {
+        values->resize(*length);
+        if (_stream->read(&(*values)[0], readSize, *length) != *length)
+        {
+            GP_ERROR("Failed to read an array of data from bundle (into a std::vector with a specified single element read size).");
+            return false;
+        }
+    }
+    return true;
+}
+
+static std::string readString(Stream* stream)
+{
+    GP_ASSERT(stream);
+
+    unsigned int length;
+    if (stream->read(&length, 4, 1) != 1)
+    {
+        GP_ERROR("Failed to read the length of a string from a bundle.");
+        return std::string();
+    }
+
+    // Sanity check to detect if string length is far too big.
+    GP_ASSERT(length < BUNDLE_MAX_STRING_LENGTH);
+
+    std::string str;
+    if (length > 0)
+    {
+        str.resize(length);
+        if (stream->read(&str[0], 1, length) != length)
+        {
+            GP_ERROR("Failed to read string from bundle.");
+            return std::string();
+        }
+    }
+    return str;
+}
+
+Bundle* Bundle::create(const char* path)
+{
+    GP_ASSERT(path);
+
+    // Search the cache for this bundle.
+    for (size_t i = 0, count = __bundleCache.size(); i < count; ++i)
+    {
+        Bundle* p = __bundleCache[i];
+        GP_ASSERT(p);
+        if (p->_path == path)
+        {
+            // Found a match
+            p->addRef();
+            return p;
+        }
+    }
+
+    // Open the bundle.
+    Stream* stream = FileSystem::open(path);
+    if (!stream)
+    {
+        GP_WARN("Failed to open file '%s'.", path);
+        return NULL;
+    }
+
+    // Read the GPB header info.
+    char sig[9];
+    if (stream->read(sig, 1, 9) != 9 || memcmp(sig, "\xABGPB\xBB\r\n\x1A\n", 9) != 0)
+    {
+        SAFE_DELETE(stream);
+        GP_WARN("Invalid GPB header for bundle '%s'.", path);
+        return NULL;
+    }
+
+    // Read version.
+    unsigned char version[2];
+    if (stream->read(version, 1, 2) != 2)
+    {
+        SAFE_DELETE(stream);
+        GP_WARN("Failed to read GPB version for bundle '%s'.", path);
+        return NULL;
+    }
+    // Check for the minimal 
+    if (version[0] != BUNDLE_VERSION_MAJOR_REQUIRED || version[1] < BUNDLE_VERSION_MINOR_REQUIRED)
+    {
+        SAFE_DELETE(stream);
+        GP_WARN("Unsupported version (%d.%d) for bundle '%s' (expected %d.%d).", (int)version[0], (int)version[1], path, BUNDLE_VERSION_MAJOR_REQUIRED, BUNDLE_VERSION_MINOR_REQUIRED);
+        return NULL;
+    }
+
+    // Read ref table.
+    unsigned int refCount;
+    if (stream->read(&refCount, 4, 1) != 1)
+    {
+        SAFE_DELETE(stream);
+        GP_WARN("Failed to read ref table for bundle '%s'.", path);
+        return NULL;
+    }
+
+    // Read all refs.
+    Reference* refs = new Reference[refCount];
+    for (unsigned int i = 0; i < refCount; ++i)
+    {
+        if ((refs[i].id = readString(stream)).empty() ||
+            stream->read(&refs[i].type, 4, 1) != 1 ||
+            stream->read(&refs[i].offset, 4, 1) != 1)
+        {
+            SAFE_DELETE(stream);
+            GP_WARN("Failed to read ref number %d for bundle '%s'.", i, path);
+            SAFE_DELETE_ARRAY(refs);
+            return NULL;
+        }
+    }
+
+    // Keep file open for faster reading later.
+    Bundle* bundle = new Bundle(path);
+    bundle->_version[0] = version[0];
+    bundle->_version[1] = version[1];
+    bundle->_referenceCount = refCount;
+    bundle->_references = refs;
+    bundle->_stream = stream;
+
+    return bundle;
+}
+
+Bundle::Reference* Bundle::find(const char* id) const
+{
+    GP_ASSERT(id);
+    GP_ASSERT(_references);
+
+    // Search the ref table for the given id (case-sensitive).
+    for (unsigned int i = 0; i < _referenceCount; ++i)
+    {
+        if (_references[i].id == id)
+        {
+            // Found a match
+            return &_references[i];
+        }
+    }
+
+    return NULL;
+}
+
+void Bundle::clearLoadSession()
+{
+    for (size_t i = 0, count = _meshSkins.size(); i < count; ++i)
+    {
+        SAFE_DELETE(_meshSkins[i]);
+    }
+    _meshSkins.clear();
+}
+
+const char* Bundle::getIdFromOffset() const
+{
+    GP_ASSERT(_stream);
+    return getIdFromOffset((unsigned int) _stream->position());
+}
+
+const char* Bundle::getIdFromOffset(unsigned int offset) const
+{
+    // Search the ref table for the given offset.
+    if (offset > 0)
+    {
+        GP_ASSERT(_references);
+        for (unsigned int i = 0; i < _referenceCount; ++i)
+        {
+            if (_references[i].offset == offset && _references[i].id.length() > 0)
+            {
+                return _references[i].id.c_str();
+            }
+        }
+    }
+    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);
+    if (ref == NULL)
+    {
+        GP_ERROR("No object with name '%s' in bundle '%s'.", id, _path.c_str());
+        return NULL;
+    }
+
+    if (ref->type != type)
+    {
+        GP_ERROR("Object '%s' in bundle '%s' has type %d (expected type %d).", id, _path.c_str(), (int)ref->type, (int)type);
+        return NULL;
+    }
+
+    // Seek to the offset of this object.
+    GP_ASSERT(_stream);
+    if (_stream->seek(ref->offset, SEEK_SET) == false)
+    {
+        GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", id, _path.c_str());
+        return NULL;
+    }
+
+    return ref;
+}
+
+Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
+{
+    GP_ASSERT(_references);
+    GP_ASSERT(_stream);
+
+    for (unsigned int i = 0; i < _referenceCount; ++i)
+    {
+        Reference* ref = &_references[i];
+        if (ref->type == type)
+        {
+            // Found a match.
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
+            {
+                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
+                return NULL;
+            }
+            return ref;
+        }
+    }
+    return NULL;
+}
+
+bool Bundle::read(unsigned int* ptr)
+{
+    return _stream->read(ptr, sizeof(unsigned int), 1) == 1;
+}
+
+bool Bundle::read(unsigned char* ptr)
+{
+    return _stream->read(ptr, sizeof(unsigned char), 1) == 1;
+}
+
+bool Bundle::read(float* ptr)
+{
+    return _stream->read(ptr, sizeof(float), 1) == 1;
+}
+
+bool Bundle::readMatrix(float* m)
+{
+    return _stream->read(m, sizeof(float), 16) == 16;
+}
+
+Scene* Bundle::loadScene(const char* id)
+{
+    clearLoadSession();
+
+    Reference* ref = NULL;
+    if (id)
+    {
+        ref = seekTo(id, BUNDLE_TYPE_SCENE);
+        if (!ref)
+        {
+            GP_ERROR("Failed to load scene with id '%s' from bundle.", id);
+            return NULL;
+        }
+    }
+    else
+    {
+        ref = seekToFirstType(BUNDLE_TYPE_SCENE);
+        if (!ref)
+        {
+            GP_ERROR("Failed to load scene from bundle; bundle contains no scene objects.");
+            return NULL;
+        }
+    }
+
+    Scene* scene = Scene::create(getIdFromOffset());
+
+    // Read the number of children.
+    unsigned int childrenCount;
+    if (!read(&childrenCount))
+    {
+        GP_ERROR("Failed to read the scene's number of children.");
+        SAFE_RELEASE(scene);
+        return NULL;
+    }
+    if (childrenCount > 0)
+    {
+        // Read each child directly into the scene.
+        for (unsigned int i = 0; i < childrenCount; i++)
+        {
+            Node* node = readNode(scene, NULL);
+            if (node)
+            {
+                scene->addNode(node);
+                node->release(); // scene now owns node
+            }
+        }
+    }
+    // Read active camera.
+    std::string xref = readString(_stream);
+    if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
+    {
+        Node* node = scene->findNode(xref.c_str() + 1, true);
+        GP_ASSERT(node);
+        Camera* camera = node->getCamera();
+        GP_ASSERT(camera);
+        scene->setActiveCamera(camera);
+    }
+
+    // Read ambient color.
+    float red, blue, green;
+    if (!read(&red))
+    {
+        GP_ERROR("Failed to read red component of the scene's ambient color in bundle '%s'.", _path.c_str());
+        SAFE_RELEASE(scene);
+        return NULL;
+    }
+    if (!read(&green))
+    {
+        GP_ERROR("Failed to read green component of the scene's ambient color in bundle '%s'.", _path.c_str());
+        SAFE_RELEASE(scene);
+        return NULL;
+    }
+    if (!read(&blue))
+    {
+        GP_ERROR("Failed to read blue component of the scene's ambient color in bundle '%s'.", _path.c_str());
+        SAFE_RELEASE(scene);
+        return NULL;
+    }
+    scene->setAmbientColor(red, green, blue);
+
+    // Parse animations.
+    GP_ASSERT(_references);
+    GP_ASSERT(_stream);
+    for (unsigned int i = 0; i < _referenceCount; ++i)
+    {
+        Reference* ref = &_references[i];
+        if (ref->type == BUNDLE_TYPE_ANIMATIONS)
+        {
+            // Found a match.
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
+            {
+                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
+                return NULL;
+            }
+            readAnimations(scene);
+        }
+    }
+
+    resolveJointReferences(scene, NULL);
+
+    return scene;
+}
+
+Node* Bundle::loadNode(const char* id)
+{
+    return loadNode(id, NULL);
+}
+
+Node* Bundle::loadNode(const char* id, Scene* sceneContext)
+{
+    GP_ASSERT(id);
+    GP_ASSERT(_references);
+    GP_ASSERT(_stream);
+
+    clearLoadSession();
+
+    // Load the node and any referenced joints with node tracking enabled.
+    _trackedNodes = new std::map<std::string, Node*>();
+    Node* node = loadNode(id, sceneContext, NULL);
+    if (node)
+        resolveJointReferences(sceneContext, node);
+
+    // Load all animations targeting any nodes or mesh skins under this node's hierarchy.
+    for (unsigned int i = 0; i < _referenceCount; i++)
+    {
+        Reference* ref = &_references[i];
+        if (ref->type == BUNDLE_TYPE_ANIMATIONS)
+        {
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
+            {
+                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
+                SAFE_DELETE(_trackedNodes);
+                return NULL;
+            }
+
+            // Read the number of animations in this object.
+            unsigned int animationCount;
+            if (!read(&animationCount))
+            {
+                GP_ERROR("Failed to read the number of animations for object '%s'.", ref->id.c_str());
+                SAFE_DELETE(_trackedNodes);
+                return NULL;
+            }
+
+            for (unsigned int j = 0; j < animationCount; j++)
+            {
+                const std::string id = readString(_stream);
+
+                // Read the number of animation channels in this animation.
+                unsigned int animationChannelCount;
+                if (!read(&animationChannelCount))
+                {
+                    GP_ERROR("Failed to read the number of animation channels for animation '%s'.", "animationChannelCount", id.c_str());
+                    SAFE_DELETE(_trackedNodes);
+                    return NULL;
+                }
+
+                Animation* animation = NULL;
+                for (unsigned int k = 0; k < animationChannelCount; k++)
+                {
+                    // Read target id.
+                    std::string targetId = readString(_stream);
+                    if (targetId.empty())
+                    {
+                        GP_ERROR("Failed to read target id for animation '%s'.", id.c_str());
+                        SAFE_DELETE(_trackedNodes);
+                        return NULL;
+                    }
+
+                    // If the target is one of the loaded nodes/joints, then load the animation.
+                    std::map<std::string, Node*>::iterator iter = _trackedNodes->find(targetId);
+                    if (iter != _trackedNodes->end())
+                    {
+                        // Read target attribute.
+                        unsigned int targetAttribute;
+                        if (!read(&targetAttribute))
+                        {
+                            GP_ERROR("Failed to read target attribute for animation '%s'.", id.c_str());
+                            SAFE_DELETE(_trackedNodes);
+                            return NULL;
+                        }
+
+                        AnimationTarget* target = iter->second;
+                        if (!target)
+                        {
+                            GP_ERROR("Failed to read %s for %s: %s", "animation target", targetId.c_str(), id.c_str());
+                            SAFE_DELETE(_trackedNodes);
+                            return NULL;
+                        }
+
+                        animation = readAnimationChannelData(animation, id.c_str(), target, targetAttribute);
+                    }
+                    else
+                    {
+                        // Skip over the target attribute.
+                        unsigned int data;
+                        if (!read(&data))
+                        {
+                            GP_ERROR("Failed to skip over target attribute for animation '%s'.", id.c_str());
+                            SAFE_DELETE(_trackedNodes);
+                            return NULL;
+                        }
+
+                        // Skip the animation channel (passing a target attribute of
+                        // 0 causes the animation to not be created).
+                        readAnimationChannelData(NULL, id.c_str(), NULL, 0);
+                    }
+                }
+            }
+        }
+    }
+
+    SAFE_DELETE(_trackedNodes);
+    return node;
+}
+
+Node* Bundle::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
+{
+    GP_ASSERT(id);
+
+    Node* node = NULL;
+
+    // Search the passed in loading contexts (scene/node) first to see
+    // if we've already loaded this node during this load session.
+    if (sceneContext)
+    {
+        node = sceneContext->findNode(id, true);
+        if (node)
+            node->addRef();
+    }
+    if (node == NULL && nodeContext)
+    {
+        node = nodeContext->findNode(id, true);
+        if (node)
+            node->addRef();
+    }
+
+    if (node == NULL)
+    {
+        // If not yet found, search the ref table and read.
+        Reference* ref = seekTo(id, BUNDLE_TYPE_NODE);
+        if (ref == NULL)
+        {
+            return NULL;
+        }
+
+        node = readNode(sceneContext, nodeContext);
+    }
+
+    return node;
+}
+
+bool Bundle::skipNode()
+{
+    const char* id = getIdFromOffset();
+    GP_ASSERT(id);
+    GP_ASSERT(_stream);
+
+    // Skip the node's type.
+    unsigned int nodeType;
+    if (!read(&nodeType))
+    {
+        GP_ERROR("Failed to skip node type for node '%s'.", id);
+        return false;
+    }
+
+    // Skip over the node's transform and parent ID.
+    if (_stream->seek(sizeof(float) * 16, SEEK_CUR) == false)
+    {
+        GP_ERROR("Failed to skip over node transform for node '%s'.", id);
+        return false;
+    }
+    readString(_stream);
+
+    // Skip over the node's children.
+    unsigned int childrenCount;
+    if (!read(&childrenCount))
+    {
+        GP_ERROR("Failed to skip over node's children count for node '%s'.", id);
+        return false;
+    }
+    else if (childrenCount > 0)
+    {
+        for (unsigned int i = 0; i < childrenCount; i++)
+        {
+            if (!skipNode())
+                return false;
+        }
+    }
+
+    // Skip over the node's camera, light, and model attachments.
+    Camera* camera = readCamera(); SAFE_RELEASE(camera);
+    Light* light = readLight(); SAFE_RELEASE(light);
+    Model* model = readModel(id); SAFE_RELEASE(model);
+
+    return true;
+}
+
+Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
+{
+    const char* id = getIdFromOffset();
+    GP_ASSERT(id);
+    GP_ASSERT(_stream);
+
+    // If we are tracking nodes and it's not in the set yet, add it.
+    if (_trackedNodes)
+    {
+        std::map<std::string, Node*>::iterator iter = _trackedNodes->find(id);
+        if (iter != _trackedNodes->end())
+        {
+            // Skip over this node since we previously read it
+            if (!skipNode())
+                return NULL;
+
+            iter->second->addRef();
+            return iter->second;
+        }
+    }
+
+    // Read node type.
+    unsigned int nodeType;
+    if (!read(&nodeType))
+    {
+        GP_ERROR("Failed to read node type for node '%s'.", id);
+        return NULL;
+    }
+
+    Node* node = NULL;
+    switch (nodeType)
+    {
+    case Node::NODE:
+        node = Node::create(id);
+        break;
+    case Node::JOINT:
+        node = Joint::create(id);
+        break;
+    default:
+        return NULL;
+    }
+
+    if (_trackedNodes)
+    {
+        // Add the new node to the list of tracked nodes
+        _trackedNodes->insert(std::make_pair(id, node));
+    }
+
+    // If no loading context is set, set this node as the loading context.
+    if (sceneContext == NULL && nodeContext == NULL)
+    {
+        nodeContext = node;
+    }
+
+    // Read transform.
+    float transform[16];
+    if (_stream->read(transform, sizeof(float), 16) != 16)
+    {
+        GP_ERROR("Failed to read transform for node '%s'.", id);
+        SAFE_RELEASE(node);
+        return NULL;
+    }
+    setTransform(transform, node);
+
+    // Skip the parent ID.
+    readString(_stream);
+
+    // Read children.
+    unsigned int childrenCount;
+    if (!read(&childrenCount))
+    {
+        GP_ERROR("Failed to read children count for node '%s'.", id);
+        SAFE_RELEASE(node);
+        return NULL;
+    }
+    if (childrenCount > 0)
+    {
+        // Read each child.
+        for (unsigned int i = 0; i < childrenCount; i++)
+        {
+            // Search the passed in loading contexts (scene/node) first to see
+            // if we've already loaded this child node during this load session.
+            Node* child = NULL;
+            id = getIdFromOffset();
+            GP_ASSERT(id);
+
+            if (sceneContext)
+            {
+                child = sceneContext->findNode(id, true);
+            }
+            if (child == NULL && nodeContext)
+            {
+                child = nodeContext->findNode(id, true);
+            }
+
+            // If the child was already loaded, skip it, otherwise read it
+            if (child)
+            {
+                skipNode();
+            }
+            else
+            {
+                child = readNode(sceneContext, nodeContext);
+            }
+
+            if (child)
+            {
+                node->addChild(child);
+                child->release(); // 'node' now owns this child
+            }
+        }
+    }
+
+    // Read camera.
+    Camera* camera = readCamera();
+    if (camera)
+    {
+        node->setCamera(camera);
+        SAFE_RELEASE(camera);
+    }
+
+    // Read light.
+    Light* light = readLight();
+    if (light)
+    {
+        node->setLight(light);
+        SAFE_RELEASE(light);
+    }
+
+    // Read model.
+    Model* model = readModel(node->getId());
+    if (model)
+    {
+        node->setModel(model);
+        SAFE_RELEASE(model);
+    }
+
+    return node;
+}
+
+Camera* Bundle::readCamera()
+{
+    unsigned char cameraType;
+    if (!read(&cameraType))
+    {
+        GP_ERROR("Failed to load camera type in bundle '%s'.", _path.c_str());
+        return NULL;
+    }
+
+    // Check if there isn't a camera to load.
+    if (cameraType == 0)
+    {
+        return NULL;
+    }
+
+    float aspectRatio;
+    if (!read(&aspectRatio))
+    {
+        GP_ERROR("Failed to load camera aspect ratio in bundle '%s'.", _path.c_str());
+        return NULL;
+    }
+
+    float nearPlane;
+    if (!read(&nearPlane))
+    {
+        GP_ERROR("Failed to load camera near plane in bundle '%s'.", _path.c_str());
+        return NULL;
+    }
+
+    float farPlane;
+    if (!read(&farPlane))
+    {
+        GP_ERROR("Failed to load camera far plane in bundle '%s'.", _path.c_str());
+        return NULL;
+    }
+
+    Camera* camera = NULL;
+    if (cameraType == Camera::PERSPECTIVE)
+    {
+        float fieldOfView;
+        if (!read(&fieldOfView))
+        {
+            GP_ERROR("Failed to load camera field of view in bundle '%s'.", _path.c_str());
+            return NULL;
+        }
+
+        camera = Camera::createPerspective(fieldOfView, aspectRatio, nearPlane, farPlane);
+    }
+    else if (cameraType == Camera::ORTHOGRAPHIC)
+    {
+        float zoomX;
+        if (!read(&zoomX))
+        {
+            GP_ERROR("Failed to load camera zoomX in bundle '%s'.", _path.c_str());
+            return NULL;
+        }
+
+        float zoomY;
+        if (!read(&zoomY))
+        {
+            GP_ERROR("Failed to load camera zoomY in bundle '%s'.", _path.c_str());
+            return NULL;
+        }
+
+        camera = Camera::createOrthographic(zoomX, zoomY, aspectRatio, nearPlane, farPlane);
+    }
+    else
+    {
+        GP_ERROR("Unsupported camera type (%d) in bundle '%s'.", cameraType, _path.c_str());
+        return NULL;
+    }
+    return camera;
+}
+
+Light* Bundle::readLight()
+{
+    unsigned char type;
+    if (!read(&type))
+    {
+        GP_ERROR("Failed to load light type in bundle '%s'.", _path.c_str());
+        return NULL;
+    }
+
+    // Check if there isn't a light to load.
+    if (type == 0)
+    {
+        return NULL;
+    }
+
+    // Read color.
+    float red, blue, green;
+    if (!read(&red) || !read(&blue) || !read(&green))
+    {
+        GP_ERROR("Failed to load light color in bundle '%s'.", _path.c_str());
+        return NULL;
+    }
+    Vector3 color(red, blue, green);
+
+    Light* light = NULL;
+    if (type == Light::DIRECTIONAL)
+    {
+        light = Light::createDirectional(color);
+    }
+    else if (type == Light::POINT)
+    {
+        float range;
+        if (!read(&range))
+        {
+            GP_ERROR("Failed to load point light range in bundle '%s'.", _path.c_str());
+            return NULL;
+        }
+        light = Light::createPoint(color, range);
+    }
+    else if (type == Light::SPOT)
+    {
+        float range, innerAngle, outerAngle;
+        if (!read(&range))
+        {
+            GP_ERROR("Failed to load spot light range in bundle '%s'.", _path.c_str());
+            return NULL;
+        }
+        if (!read(&innerAngle))
+        {
+            GP_ERROR("Failed to load spot light inner angle in bundle '%s'.", _path.c_str());
+            return NULL;
+        }
+        if (!read(&outerAngle))
+        {
+            GP_ERROR("Failed to load spot light outer angle in bundle '%s'.", _path.c_str());
+            return NULL;
+        }
+        light = Light::createSpot(color, range, innerAngle, outerAngle);
+    }
+    else
+    {
+        GP_ERROR("Unsupported light type (%d) in bundle '%s'.", type, _path.c_str());
+        return NULL;
+    }
+    return light;
+}
+
+Model* Bundle::readModel(const char* nodeId)
+{
+    std::string xref = readString(_stream);
+    if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
+    {
+        Mesh* mesh = loadMesh(xref.c_str() + 1, nodeId);
+        if (mesh)
+        {
+            Model* model = Model::create(mesh);
+            SAFE_RELEASE(mesh);
+
+            // Read skin.
+            unsigned char hasSkin;
+            if (!read(&hasSkin))
+            {
+                GP_ERROR("Failed to load whether model with mesh '%s' has a mesh skin in bundle '%s'.", xref.c_str() + 1, _path.c_str());
+                return NULL;
+            }
+            if (hasSkin)
+            {
+                MeshSkin* skin = readMeshSkin();
+                if (skin)
+                {
+                    model->setSkin(skin);
+                }
+            }
+            // Read material.
+            unsigned int materialCount;
+            if (!read(&materialCount))
+            {
+                GP_ERROR("Failed to load material count for model with mesh '%s' in bundle '%s'.", xref.c_str() + 1, _path.c_str());
+                return NULL;
+            }
+            if (materialCount > 0)
+            {
+                for (unsigned int i = 0; i < materialCount; ++i)
+                {
+                    std::string materialName = readString(_stream);
+                    std::string materialPath = getMaterialPath();
+                    if (materialPath.length() > 0)
+                    {
+                        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;
+        }
+    }
+
+    return NULL;
+}
+
+MeshSkin* Bundle::readMeshSkin()
+{
+    MeshSkin* meshSkin = new MeshSkin();
+
+    // Read bindShape.
+    float bindShape[16];
+    if (!readMatrix(bindShape))
+    {
+        GP_ERROR("Failed to load bind shape for mesh skin in bundle '%s'.", _path.c_str());
+        SAFE_DELETE(meshSkin);
+        return NULL;
+    }
+    meshSkin->setBindShape(bindShape);
+
+    MeshSkinData* skinData = new MeshSkinData();
+    skinData->skin = meshSkin;
+
+    // Read joint count.
+    unsigned int jointCount;
+    if (!read(&jointCount))
+    {
+        GP_ERROR("Failed to load joint count for mesh skin in bundle '%s'.", _path.c_str());
+        SAFE_DELETE(meshSkin);
+        SAFE_DELETE(skinData);
+        return NULL;
+    }
+    if (jointCount == 0)
+    {
+        GP_ERROR("Invalid joint count (must be greater than 0) for mesh skin in bundle '%s'.", _path.c_str());
+        SAFE_DELETE(meshSkin);
+        SAFE_DELETE(skinData);
+        return NULL;
+    }
+    meshSkin->setJointCount(jointCount);
+
+    // Read joint xref strings for all joints in the list.
+    for (unsigned int i = 0; i < jointCount; i++)
+    {
+        skinData->joints.push_back(readString(_stream));
+    }
+
+    // Read bind poses.
+    unsigned int jointsBindPosesCount;
+    if (!read(&jointsBindPosesCount))
+    {
+        GP_ERROR("Failed to load number of joint bind poses in bundle '%s'.", _path.c_str());
+        SAFE_DELETE(meshSkin);
+        SAFE_DELETE(skinData);
+        return NULL;
+    }
+    if (jointsBindPosesCount > 0)
+    {
+        GP_ASSERT(jointCount * 16 == jointsBindPosesCount);
+        float m[16];
+        for (unsigned int i = 0; i < jointCount; i++)
+        {
+            if (!readMatrix(m))
+            {
+                GP_ERROR("Failed to load joint bind pose matrix (for joint with index %d) in bundle '%s'.", i, _path.c_str());
+                SAFE_DELETE(meshSkin);
+                SAFE_DELETE(skinData);
+                return NULL;
+            }
+            skinData->inverseBindPoseMatrices.push_back(m);
+        }
+    }
+
+    // Store the MeshSkinData so we can go back and resolve all joint references later.
+    _meshSkins.push_back(skinData);
+
+    return meshSkin;
+}
+
+void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
+{
+    GP_ASSERT(_stream);
+
+    for (size_t i = 0, skinCount = _meshSkins.size(); i < skinCount; ++i)
+    {
+        MeshSkinData* skinData = _meshSkins[i];
+        GP_ASSERT(skinData);
+        GP_ASSERT(skinData->skin);
+
+        // Resolve all joints in skin joint list.
+        size_t jointCount = skinData->joints.size();
+        for (size_t j = 0; j < jointCount; ++j)
+        {
+            // TODO: Handle full xrefs (not just local # xrefs).
+            std::string jointId = skinData->joints[j];
+            if (jointId.length() > 1 && jointId[0] == '#')
+            {
+                jointId = jointId.substr(1, jointId.length() - 1);
+
+                Node* n = loadNode(jointId.c_str(), sceneContext, nodeContext);
+                if (n && n->getType() == Node::JOINT)
+                {
+                    Joint* joint = static_cast<Joint*>(n);
+                    joint->setInverseBindPose(skinData->inverseBindPoseMatrices[j]);
+                    skinData->skin->setJoint(joint, (unsigned int)j);
+                    SAFE_RELEASE(joint);
+                }
+            }
+        }
+
+        // Set the root joint.
+        if (jointCount > 0)
+        {
+            Joint* rootJoint = skinData->skin->getJoint((unsigned int)0);
+            Node* node = rootJoint;
+            GP_ASSERT(node);
+            Node* parent = node->getParent();
+
+            std::vector<Node*> loadedNodes;
+            while (true)
+            {
+                if (parent)
+                {
+                    if (skinData->skin->getJointIndex(static_cast<Joint*>(parent)) != -1)
+                    {
+                        // Parent is a joint in the MeshSkin, so treat it as the new root.
+                        rootJoint = static_cast<Joint*>(parent);
+                    }
+
+                    node = parent;
+                    parent = node->getParent();
+                }
+                else
+                {
+                    // No parent currently set for this joint.
+                    // Lookup its parentID in case it references a node that was not yet loaded as part
+                    // of the mesh skin's joint list.
+                    std::string nodeId = node->getId();
+
+                    while (true)
+                    {
+                        // Get the node's type.
+                        Reference* ref = find(nodeId.c_str());
+                        if (ref == NULL)
+                        {
+                            GP_ERROR("No object with name '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
+                            return;
+                        }
+
+                        // Seek to the current node in the file so we can get it's parent ID.
+                        seekTo(nodeId.c_str(), ref->type);
+
+                        // Skip over the node type (1 unsigned int) and transform (16 floats) and read the parent id.
+                        if (_stream->seek(sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR) == false)
+                        {
+                            GP_ERROR("Failed to skip over node type and transform for node '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
+                            return;
+                        }
+                        std::string parentID = readString(_stream);
+
+                        if (!parentID.empty())
+                            nodeId = parentID;
+                        else
+                            break;
+                    }
+
+                    if (nodeId != rootJoint->getId())
+                        loadedNodes.push_back(loadNode(nodeId.c_str(), sceneContext, nodeContext));
+
+                    break;
+                }
+            }
+
+            skinData->skin->setRootJoint(rootJoint);
+
+            // Release all the nodes that we loaded since the nodes are now owned by the mesh skin/joints.
+            for (unsigned int i = 0; i < loadedNodes.size(); i++)
+            {
+                SAFE_RELEASE(loadedNodes[i]);
+            }
+        }
+
+        // Remove the joint hierarchy from the scene since it is owned by the mesh skin.
+        if (sceneContext)
+            sceneContext->removeNode(skinData->skin->_rootNode);
+
+        // Done with this MeshSkinData entry.
+        SAFE_DELETE(_meshSkins[i]);
+    }
+    _meshSkins.clear();
+}
+
+void Bundle::readAnimation(Scene* scene)
+{
+    const std::string animationId = readString(_stream);
+
+    // Read the number of animation channels in this animation.
+    unsigned int animationChannelCount;
+    if (!read(&animationChannelCount))
+    {
+        GP_ERROR("Failed to read animation channel count for animation '%s'.", animationId.c_str());
+        return;
+    }
+
+    Animation* animation = NULL;
+    for (unsigned int i = 0; i < animationChannelCount; i++)
+    {
+        animation = readAnimationChannel(scene, animation, animationId.c_str());
+    }
+}
+
+void Bundle::readAnimations(Scene* scene)
+{
+    // Read the number of animations in this object.
+    unsigned int animationCount;
+    if (!read(&animationCount))
+    {
+        GP_ERROR("Failed to read the number of animations in the scene.");
+        return;
+    }
+
+    for (unsigned int i = 0; i < animationCount; i++)
+    {
+        readAnimation(scene);
+    }
+}
+
+Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, const char* animationId)
+{
+    GP_ASSERT(animationId);
+
+    // Read target id.
+    std::string targetId = readString(_stream);
+    if (targetId.empty())
+    {
+        GP_ERROR("Failed to read target id for animation '%s'.", animationId);
+        return NULL;
+    }
+
+    // Read target attribute.
+    unsigned int targetAttribute;
+    if (!read(&targetAttribute))
+    {
+        GP_ERROR("Failed to read target attribute for animation '%s'.", animationId);
+        return NULL;
+    }
+
+    AnimationTarget* target = NULL;
+
+    // Search for a node that matches the target.
+    if (!target)
+    {
+        target = scene->findNode(targetId.c_str());
+        if (!target)
+        {
+            GP_ERROR("Failed to find the animation target (with id '%s') for animation '%s'.", targetId.c_str(), animationId);
+            return NULL;
+        }
+    }
+
+    return readAnimationChannelData(animation, animationId, target, targetAttribute);
+}
+
+Animation* Bundle::readAnimationChannelData(Animation* animation, const char* id, AnimationTarget* target, unsigned int targetAttribute)
+{
+    GP_ASSERT(id);
+
+    std::vector<unsigned int> keyTimes;
+    std::vector<float> values;
+    std::vector<float> tangentsIn;
+    std::vector<float> tangentsOut;
+    std::vector<unsigned int> interpolation;
+
+    // Length of the arrays.
+    unsigned int keyTimesCount;
+    unsigned int valuesCount;
+    unsigned int tangentsInCount;
+    unsigned int tangentsOutCount;
+    unsigned int interpolationCount;
+
+    // Read key times.
+    if (!readArray(&keyTimesCount, &keyTimes, sizeof(unsigned int)))
+    {
+        GP_ERROR("Failed to read key times for animation '%s'.", id);
+        return NULL;
+    }
+
+    // Read key values.
+    if (!readArray(&valuesCount, &values))
+    {
+        GP_ERROR("Failed to read key values for animation '%s'.", id);
+        return NULL;
+    }
+
+    // Read in-tangents.
+    if (!readArray(&tangentsInCount, &tangentsIn))
+    {
+        GP_ERROR("Failed to read in tangents for animation '%s'.", id);
+        return NULL;
+    }
+
+    // Read out-tangents.
+    if (!readArray(&tangentsOutCount, &tangentsOut))
+    {
+        GP_ERROR("Failed to read out tangents for animation '%s'.", id);
+        return NULL;
+    }
+
+    // Read interpolations.
+    if (!readArray(&interpolationCount, &interpolation, sizeof(unsigned int)))
+    {
+        GP_ERROR("Failed to read the interpolation values for animation '%s'.", id);
+        return NULL;
+    }
+
+    if (targetAttribute > 0)
+    {
+        GP_ASSERT(target);
+        GP_ASSERT(keyTimes.size() > 0 && values.size() > 0);
+        if (animation == NULL)
+        {
+            // TODO: This code currently assumes LINEAR only.
+            animation = target->createAnimation(id, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
+        }
+        else
+        {
+            animation->createChannel(target, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
+        }
+    }
+
+    return animation;
+}
+
+Mesh* Bundle::loadMesh(const char* id)
+{
+    return loadMesh(id, NULL);
+}
+
+Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
+{
+    GP_ASSERT(_stream);
+    GP_ASSERT(id);
+
+    // Save the file position.
+    long position = _stream->position();
+    if (position == -1L)
+    {
+        GP_ERROR("Failed to save the current file position before loading mesh '%s'.", id);
+        return NULL;
+    }
+
+    // Seek to the specified mesh.
+    Reference* ref = seekTo(id, BUNDLE_TYPE_MESH);
+    if (ref == NULL)
+    {
+        GP_ERROR("Failed to locate ref for mesh '%s'.", id);
+        return NULL;
+    }
+
+    // Read mesh data.
+    MeshData* meshData = readMeshData();
+    if (meshData == NULL)
+    {
+        GP_ERROR("Failed to load mesh data for mesh '%s'.", id);
+        return NULL;
+    }
+
+    // Create mesh.
+    Mesh* mesh = Mesh::createMesh(meshData->vertexFormat, meshData->vertexCount, false);
+    if (mesh == NULL)
+    {
+        GP_ERROR("Failed to create mesh '%s'.", id);
+        SAFE_DELETE_ARRAY(meshData);
+        return NULL;
+    }
+
+    mesh->_url = _path;
+    mesh->_url += "#";
+    mesh->_url += id;
+
+    mesh->setVertexData((float*)meshData->vertexData, 0, meshData->vertexCount);
+
+    mesh->_boundingBox.set(meshData->boundingBox);
+    mesh->_boundingSphere.set(meshData->boundingSphere);
+
+    // Create mesh parts.
+    for (unsigned int i = 0; i < meshData->parts.size(); ++i)
+    {
+        MeshPartData* partData = meshData->parts[i];
+        GP_ASSERT(partData);
+
+        MeshPart* part = mesh->addPart(partData->primitiveType, partData->indexFormat, partData->indexCount, false);
+        if (part == NULL)
+        {
+            GP_ERROR("Failed to create mesh part (with index %d) for mesh '%s'.", i, id);
+            SAFE_DELETE(meshData);
+            return NULL;
+        }
+        part->setIndexData(partData->indexData, 0, partData->indexCount);
+    }
+
+    SAFE_DELETE(meshData);
+
+    // Restore file pointer.
+    if (_stream->seek(position, SEEK_SET) == false)
+    {
+        GP_ERROR("Failed to restore file pointer after loading mesh '%s'.", id);
+        return NULL;
+    }
+
+    return mesh;
+}
+
+Bundle::MeshData* Bundle::readMeshData()
+{
+    // Read vertex format/elements.
+    unsigned int vertexElementCount;
+    if (_stream->read(&vertexElementCount, 4, 1) != 1)
+    {
+        GP_ERROR("Failed to load vertex element count.");
+        return NULL;
+    }
+    if (vertexElementCount < 1)
+    {
+        GP_ERROR("Failed to load mesh data; invalid vertex element count (must be greater than 0).");
+        return NULL;
+    }
+
+    VertexFormat::Element* vertexElements = new VertexFormat::Element[vertexElementCount];
+    for (unsigned int i = 0; i < vertexElementCount; ++i)
+    {
+        unsigned int vUsage, vSize;
+        if (_stream->read(&vUsage, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to load vertex usage.");
+            SAFE_DELETE_ARRAY(vertexElements);
+            return NULL;
+        }
+        if (_stream->read(&vSize, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to load vertex size.");
+            SAFE_DELETE_ARRAY(vertexElements);
+            return NULL;
+        }
+
+        vertexElements[i].usage = (VertexFormat::Usage)vUsage;
+        vertexElements[i].size = vSize;
+    }
+
+    MeshData* meshData = new MeshData(VertexFormat(vertexElements, vertexElementCount));
+    SAFE_DELETE_ARRAY(vertexElements);
+
+    // Read vertex data.
+    unsigned int vertexByteCount;
+    if (_stream->read(&vertexByteCount, 4, 1) != 1)
+    {
+        GP_ERROR("Failed to load vertex byte count.");
+        SAFE_DELETE(meshData);
+        return NULL;
+    }
+    if (vertexByteCount == 0)
+    {
+        GP_ERROR("Failed to load mesh data; invalid vertex byte count of 0.");
+        SAFE_DELETE(meshData);
+        return NULL;
+    }
+
+    GP_ASSERT(meshData->vertexFormat.getVertexSize());
+    meshData->vertexCount = vertexByteCount / meshData->vertexFormat.getVertexSize();
+    meshData->vertexData = new unsigned char[vertexByteCount];
+    if (_stream->read(meshData->vertexData, 1, vertexByteCount) != vertexByteCount)
+    {
+        GP_ERROR("Failed to load vertex data.");
+        SAFE_DELETE(meshData);
+        return NULL;
+    }
+
+    // Read mesh bounds (bounding box and bounding sphere).
+    if (_stream->read(&meshData->boundingBox.min.x, 4, 3) != 3 || _stream->read(&meshData->boundingBox.max.x, 4, 3) != 3)
+    {
+        GP_ERROR("Failed to load mesh bounding box.");
+        SAFE_DELETE(meshData);
+        return NULL;
+    }
+    if (_stream->read(&meshData->boundingSphere.center.x, 4, 3) != 3 || _stream->read(&meshData->boundingSphere.radius, 4, 1) != 1)
+    {
+        GP_ERROR("Failed to load mesh bounding sphere.");
+        SAFE_DELETE(meshData);
+        return NULL;
+    }
+
+    // Read mesh parts.
+    unsigned int meshPartCount;
+    if (_stream->read(&meshPartCount, 4, 1) != 1)
+    {
+        GP_ERROR("Failed to load mesh part count.");
+        SAFE_DELETE(meshData);
+        return NULL;
+    }
+    for (unsigned int i = 0; i < meshPartCount; ++i)
+    {
+        // Read primitive type, index format and index count.
+        unsigned int pType, iFormat, iByteCount;
+        if (_stream->read(&pType, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to load primitive type for mesh part with index %d.", i);
+            SAFE_DELETE(meshData);
+            return NULL;
+        }
+        if (_stream->read(&iFormat, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to load index format for mesh part with index %d.", i);
+            SAFE_DELETE(meshData);
+            return NULL;
+        }
+        if (_stream->read(&iByteCount, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to load index byte count for mesh part with index %d.", i);
+            SAFE_DELETE(meshData);
+            return NULL;
+        }
+
+        MeshPartData* partData = new MeshPartData();
+        meshData->parts.push_back(partData);
+
+        partData->primitiveType = (Mesh::PrimitiveType)pType;
+        partData->indexFormat = (Mesh::IndexFormat)iFormat;
+
+        unsigned int indexSize = 0;
+        switch (partData->indexFormat)
+        {
+        case Mesh::INDEX8:
+            indexSize = 1;
+            break;
+        case Mesh::INDEX16:
+            indexSize = 2;
+            break;
+        case Mesh::INDEX32:
+            indexSize = 4;
+            break;
+        default:
+            GP_ERROR("Unsupported index format for mesh part with index %d.", i);
+            return NULL;
+        }
+
+        GP_ASSERT(indexSize);
+        partData->indexCount = iByteCount / indexSize;
+
+        partData->indexData = new unsigned char[iByteCount];
+        if (_stream->read(partData->indexData, 1, iByteCount) != iByteCount)
+        {
+            GP_ERROR("Failed to read index data for mesh part with index %d.", i);
+            SAFE_DELETE(meshData);
+            return NULL;
+        }
+    }
+
+    return meshData;
+}
+
+Bundle::MeshData* Bundle::readMeshData(const char* url)
+{
+    GP_ASSERT(url);
+
+    size_t len = strlen(url);
+    if (len == 0)
+    {
+        GP_ERROR("Mesh data URL must be non-empty.");
+        return NULL;
+    }
+
+    // Parse URL (formatted as 'bundle#id').
+    std::string urlstring(url);
+    size_t pos = urlstring.find('#');
+    if (pos == std::string::npos)
+    {
+        GP_ERROR("Invalid mesh data URL '%s' (must be of the form 'bundle#id').", url);
+        return NULL;
+    }
+
+    std::string file = urlstring.substr(0, pos);
+    std::string id = urlstring.substr(pos + 1);
+
+    // Load bundle.
+    Bundle* bundle = Bundle::create(file.c_str());
+    if (bundle == NULL)
+    {
+        GP_ERROR("Failed to load bundle '%s'.", file.c_str());
+        return NULL;
+    }
+
+    // Seek to mesh with specified ID in bundle.
+    Reference* ref = bundle->seekTo(id.c_str(), BUNDLE_TYPE_MESH);
+    if (ref == NULL)
+    {
+        GP_ERROR("Failed to load ref from bundle '%s' for mesh with id '%s'.", file.c_str(), id.c_str());
+        return NULL;
+    }
+
+    // Read mesh data from current file position.
+    MeshData* meshData = bundle->readMeshData();
+
+    SAFE_RELEASE(bundle);
+
+    return meshData;
+}
+
+Font* Bundle::loadFont(const char* id)
+{
+    GP_ASSERT(id);
+    GP_ASSERT(_stream);
+
+    // Seek to the specified font.
+    Reference* ref = seekTo(id, BUNDLE_TYPE_FONT);
+    if (ref == NULL)
+    {
+        GP_ERROR("Failed to load ref for font '%s'.", id);
+        return NULL;
+    }
+
+    // Read font family.
+    std::string family = readString(_stream);
+    if (family.empty())
+    {
+        GP_ERROR("Failed to read font family for font '%s'.", id);
+        return NULL;
+    }
+
+    // Read font style
+    unsigned int style;
+    if (_stream->read(&style, 4, 1) != 1)
+    {
+        GP_ERROR("Failed to read style for font '%s'.", id);
+        return NULL;
+    }
+
+    // In bundle version 1.4 we introduced storing multiple font sizes per font
+    unsigned int fontSizeCount = 1;
+    if (getVersionMajor() >= 1 && getVersionMinor() >= 4)
+    {
+        if (_stream->read(&fontSizeCount, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to read font size count for font '%s'.", id);
+            return NULL;
+        }
+    }
+
+    Font* masterFont = NULL;
+
+    for (unsigned int i = 0; i < fontSizeCount; ++i)
+    {
+        // Read font size
+        unsigned int size;
+        if (_stream->read(&size, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to read size for font '%s'.", id);
+            return NULL;
+        }
+
+        // Read character set.
+        std::string charset = readString(_stream);
+
+        // Read font glyphs.
+        unsigned int glyphCount;
+        if (_stream->read(&glyphCount, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to read glyph count for font '%s'.", id);
+            return NULL;
+        }
+        if (glyphCount == 0)
+        {
+            GP_ERROR("Invalid glyph count (must be greater than 0) for font '%s'.", id);
+            return NULL;
+        }
+
+        Font::Glyph* glyphs = new Font::Glyph[glyphCount];
+        if (_stream->read(glyphs, sizeof(Font::Glyph), glyphCount) != glyphCount)
+        {
+            GP_ERROR("Failed to read glyphs for font '%s'.", id);
+            SAFE_DELETE_ARRAY(glyphs);
+            return NULL;
+        }
+
+        // Read texture attributes.
+        unsigned int width, height, textureByteCount;
+        if (_stream->read(&width, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to read texture width for font '%s'.", id);
+            SAFE_DELETE_ARRAY(glyphs);
+            return NULL;
+        }
+        if (_stream->read(&height, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to read texture height for font '%s'.", id);
+            SAFE_DELETE_ARRAY(glyphs);
+            return NULL;
+        }
+        if (_stream->read(&textureByteCount, 4, 1) != 1)
+        {
+            GP_ERROR("Failed to read texture byte count for font '%s'.", id);
+            SAFE_DELETE_ARRAY(glyphs);
+            return NULL;
+        }
+        if (textureByteCount != (width * height))
+        {
+            GP_ERROR("Invalid texture byte count for font '%s'.", id);
+            SAFE_DELETE_ARRAY(glyphs);
+            return NULL;
+        }
+
+        // Read texture data.
+        unsigned char* textureData = new unsigned char[textureByteCount];
+        if (_stream->read(textureData, 1, textureByteCount) != textureByteCount)
+        {
+            GP_ERROR("Failed to read texture data for font '%s'.", id);
+            SAFE_DELETE_ARRAY(glyphs);
+            SAFE_DELETE_ARRAY(textureData);
+            return NULL;
+        }
+
+        unsigned int format = Font::BITMAP;
+
+        // In bundle version 1.3 we added a format field
+        if (getVersionMajor() >= 1 && getVersionMinor() >= 3)
+        {
+            if (_stream->read(&format, 4, 1) != 1)
+            {
+                GP_ERROR("Failed to font format'%u'.", format);
+                SAFE_DELETE_ARRAY(glyphs);
+                SAFE_DELETE_ARRAY(textureData);
+                return NULL;
+            }
+        }
+
+        // Create the texture for the font.
+        Texture* texture = Texture::create(Texture::ALPHA, width, height, textureData, true);
+
+        // Free the texture data (no longer needed).
+        SAFE_DELETE_ARRAY(textureData);
+
+        if (texture == NULL)
+        {
+            GP_ERROR("Failed to create texture for font '%s'.", id);
+            SAFE_DELETE_ARRAY(glyphs);
+            return NULL;
+        }
+
+        // Create the font for this size
+        Font* font = Font::create(family.c_str(), Font::PLAIN, size, glyphs, glyphCount, texture, (Font::Format)format);
+
+        // Free the glyph array.
+        SAFE_DELETE_ARRAY(glyphs);
+
+        // Release the texture since the Font now owns it.
+        SAFE_RELEASE(texture);
+
+        if (font)
+        {
+            font->_path = _path;
+            font->_id = id;
+
+            if (masterFont)
+                masterFont->_sizes.push_back(font);
+            else
+                masterFont = font;
+        }
+    }
+
+    return masterFont;
+}
+
+void Bundle::setTransform(const float* values, Transform* transform)
+{
+    GP_ASSERT(transform);
+
+    // Load array into transform.
+    Matrix matrix(values);
+    Vector3 scale, translation;
+    Quaternion rotation;
+    matrix.decompose(&scale, &rotation, &translation);
+    transform->setScale(scale);
+    transform->setTranslation(translation);
+    transform->setRotation(rotation);
+}
+
+bool Bundle::contains(const char* id) const
+{
+    return (find(id) != NULL);
+}
+
+unsigned int Bundle::getObjectCount() const
+{
+    return _referenceCount;
+}
+
+const char* Bundle::getObjectId(unsigned int index) const
+{
+    GP_ASSERT(_references);
+    return (index >= _referenceCount ? NULL : _references[index].id.c_str());
+}
+
+Bundle::Reference::Reference()
+    : type(0), offset(0)
+{
+}
+
+Bundle::Reference::~Reference()
+{
+}
+
+Bundle::MeshPartData::MeshPartData() :
+    indexCount(0), indexData(NULL)
+{
+}
+
+Bundle::MeshPartData::~MeshPartData()
+{
+    SAFE_DELETE_ARRAY(indexData);
+}
+
+Bundle::MeshData::MeshData(const VertexFormat& vertexFormat)
+    : vertexFormat(vertexFormat), vertexCount(0), vertexData(NULL)
+{
+}
+
+Bundle::MeshData::~MeshData()
+{
+    SAFE_DELETE_ARRAY(vertexData);
+
+    for (unsigned int i = 0; i < parts.size(); ++i)
+    {
+        SAFE_DELETE(parts[i]);
+    }
+}
+
+}

+ 465 - 448
gameplay/src/Bundle.h

@@ -1,448 +1,465 @@
-#ifndef BUNDLE_H_
-#define BUNDLE_H_
-
-#include "Mesh.h"
-#include "Font.h"
-#include "Node.h"
-#include "Game.h"
-
-namespace gameplay
-{
-
-/**
- * Represents a gameplay bundle file (.gpb) that contains a
- * collection of binary game assets that can be loaded.
- */
-class Bundle : public Ref
-{
-    friend class PhysicsController;
-    friend class SceneLoader;
-
-public:
-
-    /**
-     * Returns a Bundle for the given resource path.
-     *
-     * The specified path must reference a valid gameplay bundle file.
-     * If the bundle is already loaded, the existing bundle is returned
-     * with its reference count incremented. When no longer needed, the
-     * release() method must be called. Note that calling release() does
-     * NOT free any actual game objects created/returned from the Bundle
-     * instance and those objects must be released separately.
-     * @script{create}
-     */
-    static Bundle* create(const char* path);
-
-    /**
-     * Loads the scene with the specified ID from the bundle.
-     * If id is NULL then the first scene found is loaded.
-     * 
-     * @param id The ID of the scene to load (NULL to load the first scene).
-     * 
-     * @return The loaded scene, or NULL if the scene could not be loaded.
-     * @script{create}
-     */
-    Scene* loadScene(const char* id = NULL);
-
-    /**
-     * Loads a node with the specified ID from the bundle.
-     *
-     * @param id The ID of the node to load in the bundle.
-     * 
-     * @return The loaded node, or NULL if the node could not be loaded.
-     * @script{create}
-     */
-    Node* loadNode(const char* id);
-
-    /**
-     * Loads a mesh with the specified ID from the bundle.
-     *
-     * @param id The ID of the mesh to load.
-     * 
-     * @return The loaded mesh, or NULL if the mesh could not be loaded.
-     * @script{create}
-     */
-    Mesh* loadMesh(const char* id);
-
-    /**
-     * Loads a font with the specified ID from the bundle.
-     *
-     * @param id The ID of the font to load.
-     * 
-     * @return The loaded font, or NULL if the font could not be loaded.
-     * @script{create}
-     */
-    Font* loadFont(const char* id);
-
-    /**
-     * Determines if this bundle contains a top-level object with the given ID.
-     *
-     * This method performs a case-sensitive comparison.
-     *
-     * @param id The ID of the object to search for.
-     */
-    bool contains(const char* id) const;
-
-    /**
-     * Returns the number of top-level objects in this bundle.
-     */
-    unsigned int getObjectCount() const;
-
-    /**
-     * Returns the unique identifier of the top-level object at the specified index in this bundle.
-     *
-     * @param index The index of the object.
-     * 
-     * @return The ID of the object at the given index, or NULL if index is invalid.
-     */
-    const char* getObjectId(unsigned int index) const;
-
-private:
-
-    class Reference
-    {
-    public:
-        std::string id;
-        unsigned int type;
-        unsigned int offset;
-
-        /**
-         * Constructor.
-         */
-        Reference();
-
-        /**
-         * Destructor.
-         */
-        ~Reference();
-    };
-
-    struct MeshSkinData
-    {
-        MeshSkin* skin;
-        std::vector<std::string> joints;
-        std::vector<Matrix> inverseBindPoseMatrices;
-    };
-
-    struct MeshPartData
-    {
-        MeshPartData();
-        ~MeshPartData();
-
-        Mesh::PrimitiveType primitiveType;
-        Mesh::IndexFormat indexFormat;
-        unsigned int indexCount;
-        unsigned char* indexData;
-    };
-
-    struct MeshData
-    {
-        MeshData(const VertexFormat& vertexFormat);
-        ~MeshData();
-
-        VertexFormat vertexFormat;
-        unsigned int vertexCount;
-        unsigned char* vertexData;
-        BoundingBox boundingBox;
-        BoundingSphere boundingSphere;
-        Mesh::PrimitiveType primitiveType;
-        std::vector<MeshPartData*> parts;
-    };
-
-    Bundle(const char* path);
-
-    /**
-     * Destructor.
-     */
-    ~Bundle();
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    Bundle& operator=(const Bundle&);
-
-    /**
-     * Finds a reference by ID.
-     */
-    Reference* find(const char* id) const;
-
-    /**
-     * Resets any load session specific state for the bundle.
-     */
-    void clearLoadSession();
-
-    /**
-     * Returns the ID of the object at the current file position.
-     * Returns NULL if not found.
-     * 
-     * @return The ID string or NULL if not found.
-     */
-    const char* getIdFromOffset() const;
-
-    /**
-     * Returns the ID of the object at the given file offset by searching through the reference table.
-     * Returns NULL if not found.
-     *
-     * @param offset The file offset.
-     * 
-     * @return The ID string or NULL if not found.
-     */
-    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.
-     *
-     * @param id The ID string to search for.
-     * @param type The object type.
-     * 
-     * @return The reference object or NULL if there was an error.
-     */
-    Reference* seekTo(const char* id, unsigned int type);
-
-    /**
-     * Seeks the file pointer to the first object that matches the given type.
-     * 
-     * @param type The object type.
-     * 
-     * @return The reference object or NULL if there was an error.
-     */
-    Reference* seekToFirstType(unsigned int type);
-
-    /**
-     * Internal method to load a node.
-     *
-     * Only one of node or scene should be passed as non-NULL (or neither).
-     */
-    Node* loadNode(const char* id, Scene* sceneContext, Node* nodeContext);
-
-    /**
-     * Internal method for SceneLoader to load a node into a scene.
-     */
-    Node* loadNode(const char* id, Scene* sceneContext);
-
-    /**
-     * Loads a mesh with the specified ID from the bundle.
-     *
-     * @param id The ID of the mesh to load.
-     * @param nodeId The id of the mesh's model's parent node.
-     * 
-     * @return The loaded mesh, or NULL if the mesh could not be loaded.
-     */
-    Mesh* loadMesh(const char* id, const char* nodeId);
-
-    /**
-     * Reads an unsigned int from the current file position.
-     *
-     * @param ptr A pointer to load the value into.
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    bool read(unsigned int* ptr);
-
-    /**
-     * Reads an unsigned char from the current file position.
-     * 
-     * @param ptr A pointer to load the value into.
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    bool read(unsigned char* ptr);
-
-    /**
-     * Reads a float from the current file position.
-     * 
-     * @param ptr A pointer to load the value into.
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    bool read(float* ptr);
-
-    /**
-     * Reads an array of values and the array length from the current file position.
-     * 
-     * @param length A pointer to where the length of the array will be copied to.
-     * @param ptr A pointer to the array where the data will be copied to.
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    template <class T>
-    bool readArray(unsigned int* length, T** ptr);
-
-    /**
-     * Reads an array of values and the array length from the current file position.
-     * 
-     * @param length A pointer to where the length of the array will be copied to.
-     * @param values A pointer to the vector to copy the values to. The vector will be resized if it is smaller than length.
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    template <class T>
-    bool readArray(unsigned int* length, std::vector<T>* values);
-
-    /**
-     * Reads an array of values and the array length from the current file position.
-     * 
-     * @param length A pointer to where the length of the array will be copied to.
-     * @param values A pointer to the vector to copy the values to. The vector will be resized if it is smaller than length.
-     * @param readSize The size that reads will be performed at, size must be the same as or smaller then the sizeof(T)
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    template <class T>
-    bool readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize);
-    
-    /**
-     * Reads 16 floats from the current file position.
-     *
-     * @param m A pointer to float array of size 16.
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    bool readMatrix(float* m);
-
-    /**
-     * Reads an xref string from the current file position.
-     * 
-     * @param id The string to load the ID string into.
-     * 
-     * @return True if successful, false if an error occurred.
-     */
-    bool readXref(std::string& id);
-
-    /**
-     * Recursively reads nodes from the current file position.
-     * This method will load cameras, lights and models in the nodes.
-     * 
-     * @return A pointer to new node or NULL if there was an error.
-     */
-    Node* readNode(Scene* sceneContext, Node* nodeContext);
-
-    /**
-     * Reads a camera from the current file position.
-     *
-     * @return A pointer to a new camera or NULL if there was an error.
-     */
-    Camera* readCamera();
-
-    /**
-     * Reads a light from the current file position.
-     *
-     * @return A pointer to a new light or NULL if there was an error.
-     */
-    Light* readLight();
-
-    /**
-     * Reads a model from the current file position.
-     * 
-     * @return A pointer to a new model or NULL if there was an error.
-     */
-    Model* readModel(const char* nodeId);
-
-    /**
-     * Reads mesh data from the current file position.
-     */
-    MeshData* readMeshData();
-
-    /**
-     * Reads mesh data for the specified URL.
-     *
-     * The specified URL should be formatted as 'bundle#id', where
-     * 'bundle' is the bundle file containing the mesh and 'id' is the ID
-     * of the mesh to read data for.
-     *
-     * @param url The URL to read mesh data from.
-     *
-     * @return The mesh rigid body data.
-     */
-    static MeshData* readMeshData(const char* url);
-
-    /**
-     * Reads a mesh skin from the current file position.
-     *
-     * @return A pointer to a new mesh skin or NULL if there was an error.
-     */
-    MeshSkin* readMeshSkin();
-
-    /**
-     * Reads an animation from the current file position.
-     * 
-     * @param scene The scene to load the animations into.
-     */
-    void readAnimation(Scene* scene);
-
-    /**
-     * Reads an "animations" object from the current file position and all of the animations contained in it.
-     * 
-     * @param scene The scene to load the animations into.
-     */
-    void readAnimations(Scene* scene);
-
-    /**
-     * Reads an animation channel at the current file position into the given animation.
-     * 
-     * @param scene The scene that the animation is in.
-     * @param animation The animation to the load channel into.
-     * @param animationId The ID of the animation that this channel is loaded into.
-     * 
-     * @return The animation that the channel was loaded into.
-     */
-    Animation* readAnimationChannel(Scene* scene, Animation* animation, const char* animationId);
-
-    /**
-     * Reads the animation channel data at the current file position into the given animation
-     * (with the given animation target and target attribute).
-     * 
-     * Note: this is used by Bundle::loadNode(const char*, Scene*) and Bundle::readAnimationChannel(Scene*, Animation*, const char*).
-     * 
-     * @param animation The animation to the load channel into.
-     * @param id The ID of the animation that this channel is loaded into.
-     * @param target The animation target.
-     * @param targetAttribute The target attribute being animated.
-     * 
-     * @return The animation that the channel was loaded into.
-     */
-    Animation* readAnimationChannelData(Animation* animation, const char* id, AnimationTarget* target, unsigned int targetAttribute);
-
-    /**
-     * Sets the transformation matrix.
-     *
-     * @param values A pointer to array of 16 floats.
-     * @param transform The transform to set the values in.
-     */
-    void setTransform(const float* values, Transform* transform);
-
-    /**
-     * Resolves joint references for all pending mesh skins.
-     */
-    void resolveJointReferences(Scene* sceneContext, Node* nodeContext);
-
-private:
-
-    /**
-     * Skips over a Node's data within a bundle.
-     *
-     * @return True if the Node was successfully skipped; false otherwise.
-     */
-    bool skipNode();
-
-    std::string _path;
-    std::string _materialPath;
-    unsigned int _referenceCount;
-    Reference* _references;
-    Stream* _stream;
-
-    std::vector<MeshSkinData*> _meshSkins;
-    std::map<std::string, Node*>* _trackedNodes;
-};
-
-}
-
-#endif
+#ifndef BUNDLE_H_
+#define BUNDLE_H_
+
+#include "Mesh.h"
+#include "Font.h"
+#include "Node.h"
+#include "Game.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a gameplay bundle file (.gpb) that contains a
+ * collection of binary game assets that can be loaded.
+ */
+class Bundle : public Ref
+{
+    friend class PhysicsController;
+    friend class SceneLoader;
+
+public:
+
+    /**
+     * Returns a Bundle for the given resource path.
+     *
+     * The specified path must reference a valid gameplay bundle file.
+     * If the bundle is already loaded, the existing bundle is returned
+     * with its reference count incremented. When no longer needed, the
+     * release() method must be called. Note that calling release() does
+     * NOT free any actual game objects created/returned from the Bundle
+     * instance and those objects must be released separately.
+     * 
+     * @return The new Bundle or NULL if there was an error.
+     * @script{create}
+     */
+    static Bundle* create(const char* path);
+
+    /**
+     * Loads the scene with the specified ID from the bundle.
+     * If id is NULL then the first scene found is loaded.
+     * 
+     * @param id The ID of the scene to load (NULL to load the first scene).
+     * 
+     * @return The loaded scene, or NULL if the scene could not be loaded.
+     * @script{create}
+     */
+    Scene* loadScene(const char* id = NULL);
+
+    /**
+     * Loads a node with the specified ID from the bundle.
+     *
+     * @param id The ID of the node to load in the bundle.
+     * 
+     * @return The loaded node, or NULL if the node could not be loaded.
+     * @script{create}
+     */
+    Node* loadNode(const char* id);
+
+    /**
+     * Loads a mesh with the specified ID from the bundle.
+     *
+     * @param id The ID of the mesh to load.
+     * 
+     * @return The loaded mesh, or NULL if the mesh could not be loaded.
+     * @script{create}
+     */
+    Mesh* loadMesh(const char* id);
+
+    /**
+     * Loads a font with the specified ID from the bundle.
+     *
+     * @param id The ID of the font to load.
+     * 
+     * @return The loaded font, or NULL if the font could not be loaded.
+     * @script{create}
+     */
+    Font* loadFont(const char* id);
+
+    /**
+     * Determines if this bundle contains a top-level object with the given ID.
+     *
+     * This method performs a case-sensitive comparison.
+     *
+     * @param id The ID of the object to search for.
+     */
+    bool contains(const char* id) const;
+
+    /**
+     * Returns the number of top-level objects in this bundle.
+     */
+    unsigned int getObjectCount() const;
+
+    /**
+     * Gets the unique identifier of the top-level object at the specified index in this bundle.
+     *
+     * @param index The index of the object.
+     * 
+     * @return The ID of the object at the given index, or NULL if index is invalid.
+     */
+    const char* getObjectId(unsigned int index) const;
+
+    /**
+     * Gets the major version of the loaded bundle.
+     *
+     * @return The major version of the loaded bundle.
+     */
+    unsigned int getVersionMajor() const;
+
+    /**
+     * Gets the minor version of the loaded bundle.
+     *
+     * @return The minor version of the loaded bundle.
+     */
+    unsigned int getVersionMinor() const;
+
+private:
+
+    class Reference
+    {
+    public:
+        std::string id;
+        unsigned int type;
+        unsigned int offset;
+
+        /**
+         * Constructor.
+         */
+        Reference();
+
+        /**
+         * Destructor.
+         */
+        ~Reference();
+    };
+
+    struct MeshSkinData
+    {
+        MeshSkin* skin;
+        std::vector<std::string> joints;
+        std::vector<Matrix> inverseBindPoseMatrices;
+    };
+
+    struct MeshPartData
+    {
+        MeshPartData();
+        ~MeshPartData();
+
+        Mesh::PrimitiveType primitiveType;
+        Mesh::IndexFormat indexFormat;
+        unsigned int indexCount;
+        unsigned char* indexData;
+    };
+
+    struct MeshData
+    {
+        MeshData(const VertexFormat& vertexFormat);
+        ~MeshData();
+
+        VertexFormat vertexFormat;
+        unsigned int vertexCount;
+        unsigned char* vertexData;
+        BoundingBox boundingBox;
+        BoundingSphere boundingSphere;
+        Mesh::PrimitiveType primitiveType;
+        std::vector<MeshPartData*> parts;
+    };
+
+    Bundle(const char* path);
+
+    /**
+     * Destructor.
+     */
+    ~Bundle();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Bundle& operator=(const Bundle&);
+
+    /**
+     * Finds a reference by ID.
+     */
+    Reference* find(const char* id) const;
+
+    /**
+     * Resets any load session specific state for the bundle.
+     */
+    void clearLoadSession();
+
+    /**
+     * Returns the ID of the object at the current file position.
+     * Returns NULL if not found.
+     * 
+     * @return The ID string or NULL if not found.
+     */
+    const char* getIdFromOffset() const;
+
+    /**
+     * Returns the ID of the object at the given file offset by searching through the reference table.
+     * Returns NULL if not found.
+     *
+     * @param offset The file offset.
+     * 
+     * @return The ID string or NULL if not found.
+     */
+    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.
+     *
+     * @param id The ID string to search for.
+     * @param type The object type.
+     * 
+     * @return The reference object or NULL if there was an error.
+     */
+    Reference* seekTo(const char* id, unsigned int type);
+
+    /**
+     * Seeks the file pointer to the first object that matches the given type.
+     * 
+     * @param type The object type.
+     * 
+     * @return The reference object or NULL if there was an error.
+     */
+    Reference* seekToFirstType(unsigned int type);
+
+    /**
+     * Internal method to load a node.
+     *
+     * Only one of node or scene should be passed as non-NULL (or neither).
+     */
+    Node* loadNode(const char* id, Scene* sceneContext, Node* nodeContext);
+
+    /**
+     * Internal method for SceneLoader to load a node into a scene.
+     */
+    Node* loadNode(const char* id, Scene* sceneContext);
+
+    /**
+     * Loads a mesh with the specified ID from the bundle.
+     *
+     * @param id The ID of the mesh to load.
+     * @param nodeId The id of the mesh's model's parent node.
+     * 
+     * @return The loaded mesh, or NULL if the mesh could not be loaded.
+     */
+    Mesh* loadMesh(const char* id, const char* nodeId);
+
+    /**
+     * Reads an unsigned int from the current file position.
+     *
+     * @param ptr A pointer to load the value into.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    bool read(unsigned int* ptr);
+
+    /**
+     * Reads an unsigned char from the current file position.
+     * 
+     * @param ptr A pointer to load the value into.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    bool read(unsigned char* ptr);
+
+    /**
+     * Reads a float from the current file position.
+     * 
+     * @param ptr A pointer to load the value into.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    bool read(float* ptr);
+
+    /**
+     * Reads an array of values and the array length from the current file position.
+     * 
+     * @param length A pointer to where the length of the array will be copied to.
+     * @param ptr A pointer to the array where the data will be copied to.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    template <class T>
+    bool readArray(unsigned int* length, T** ptr);
+
+    /**
+     * Reads an array of values and the array length from the current file position.
+     * 
+     * @param length A pointer to where the length of the array will be copied to.
+     * @param values A pointer to the vector to copy the values to. The vector will be resized if it is smaller than length.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    template <class T>
+    bool readArray(unsigned int* length, std::vector<T>* values);
+
+    /**
+     * Reads an array of values and the array length from the current file position.
+     * 
+     * @param length A pointer to where the length of the array will be copied to.
+     * @param values A pointer to the vector to copy the values to. The vector will be resized if it is smaller than length.
+     * @param readSize The size that reads will be performed at, size must be the same as or smaller then the sizeof(T)
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    template <class T>
+    bool readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize);
+    
+    /**
+     * Reads 16 floats from the current file position.
+     *
+     * @param m A pointer to float array of size 16.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    bool readMatrix(float* m);
+
+    /**
+     * Reads an xref string from the current file position.
+     * 
+     * @param id The string to load the ID string into.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
+    bool readXref(std::string& id);
+
+    /**
+     * Recursively reads nodes from the current file position.
+     * This method will load cameras, lights and models in the nodes.
+     * 
+     * @return A pointer to new node or NULL if there was an error.
+     */
+    Node* readNode(Scene* sceneContext, Node* nodeContext);
+
+    /**
+     * Reads a camera from the current file position.
+     *
+     * @return A pointer to a new camera or NULL if there was an error.
+     */
+    Camera* readCamera();
+
+    /**
+     * Reads a light from the current file position.
+     *
+     * @return A pointer to a new light or NULL if there was an error.
+     */
+    Light* readLight();
+
+    /**
+     * Reads a model from the current file position.
+     * 
+     * @return A pointer to a new model or NULL if there was an error.
+     */
+    Model* readModel(const char* nodeId);
+
+    /**
+     * Reads mesh data from the current file position.
+     */
+    MeshData* readMeshData();
+
+    /**
+     * Reads mesh data for the specified URL.
+     *
+     * The specified URL should be formatted as 'bundle#id', where
+     * 'bundle' is the bundle file containing the mesh and 'id' is the ID
+     * of the mesh to read data for.
+     *
+     * @param url The URL to read mesh data from.
+     *
+     * @return The mesh rigid body data.
+     */
+    static MeshData* readMeshData(const char* url);
+
+    /**
+     * Reads a mesh skin from the current file position.
+     *
+     * @return A pointer to a new mesh skin or NULL if there was an error.
+     */
+    MeshSkin* readMeshSkin();
+
+    /**
+     * Reads an animation from the current file position.
+     * 
+     * @param scene The scene to load the animations into.
+     */
+    void readAnimation(Scene* scene);
+
+    /**
+     * Reads an "animations" object from the current file position and all of the animations contained in it.
+     * 
+     * @param scene The scene to load the animations into.
+     */
+    void readAnimations(Scene* scene);
+
+    /**
+     * Reads an animation channel at the current file position into the given animation.
+     * 
+     * @param scene The scene that the animation is in.
+     * @param animation The animation to the load channel into.
+     * @param animationId The ID of the animation that this channel is loaded into.
+     * 
+     * @return The animation that the channel was loaded into.
+     */
+    Animation* readAnimationChannel(Scene* scene, Animation* animation, const char* animationId);
+
+    /**
+     * Reads the animation channel data at the current file position into the given animation
+     * (with the given animation target and target attribute).
+     * 
+     * Note: this is used by Bundle::loadNode(const char*, Scene*) and Bundle::readAnimationChannel(Scene*, Animation*, const char*).
+     * 
+     * @param animation The animation to the load channel into.
+     * @param id The ID of the animation that this channel is loaded into.
+     * @param target The animation target.
+     * @param targetAttribute The target attribute being animated.
+     * 
+     * @return The animation that the channel was loaded into.
+     */
+    Animation* readAnimationChannelData(Animation* animation, const char* id, AnimationTarget* target, unsigned int targetAttribute);
+
+    /**
+     * Sets the transformation matrix.
+     *
+     * @param values A pointer to array of 16 floats.
+     * @param transform The transform to set the values in.
+     */
+    void setTransform(const float* values, Transform* transform);
+
+    /**
+     * Resolves joint references for all pending mesh skins.
+     */
+    void resolveJointReferences(Scene* sceneContext, Node* nodeContext);
+
+private:
+
+    /**
+     * Skips over a Node's data within a bundle.
+     *
+     * @return True if the Node was successfully skipped; false otherwise.
+     */
+    bool skipNode();
+
+    unsigned char _version[2];
+    std::string _path;
+    std::string _materialPath;
+    unsigned int _referenceCount;
+    Reference* _references;
+    Stream* _stream;
+
+    std::vector<MeshSkinData*> _meshSkins;
+    std::map<std::string, Node*>* _trackedNodes;
+};
+
+}
+
+#endif

+ 63 - 158
gameplay/src/Button.cpp

@@ -1,158 +1,63 @@
-#include "Base.h"
-#include "Button.h"
-#include "Gamepad.h"
-
-namespace gameplay
-{
-
-Button::Button() : _dataBinding(0)
-{
-}
-
-Button::~Button()
-{
-}
-
-Button* Button::create(const char* id, Theme::Style* style)
-{
-    Button* button = new Button();
-
-    button->_id = id;
-    button->_style = style;
-
-    return button;
-}
-
-Button* Button::create(Theme::Style* style, Properties* properties)
-{
-    Button* button = new Button();
-    button->initialize(style, properties);
-
-    // Different types of data bindings can be named differently in a button namespace.
-    // Gamepad button mappings have the name "mapping" and correspond to Gamepad::ButtonMapping enums.
-    const char* mapping = properties->getString("mapping");
-    if (mapping)
-    {
-        button->_dataBinding = Gamepad::getButtonMappingFromString(mapping);
-    }
-
-    return button;
-}
-
-bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    switch (evt)
-    {
-    case Touch::TOUCH_PRESS:
-        if (_contactIndex == INVALID_CONTACT_INDEX)
-        {
-            if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
-                y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
-            {
-                _contactIndex = (int) contactIndex;
-                notifyListeners(Control::Listener::PRESS);
-                setState(Control::ACTIVE);
-                return _consumeInputEvents;
-            }
-            else
-            {
-                setState(Control::NORMAL);
-            }
-        }
-        break;
-
-    case Touch::TOUCH_RELEASE:
-        if (_contactIndex == (int) contactIndex)
-        {
-            _contactIndex = INVALID_CONTACT_INDEX;
-            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);
-            }
-            else
-            {
-                setState(Control::NORMAL);
-            }
-            return _consumeInputEvents;
-        }
-        break;
-    case Touch::TOUCH_MOVE:
-        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";
-}
-
-const unsigned int Button::getDataBinding() const
-{
-    return _dataBinding;
-}
-
-void Button::setDataBinding(unsigned int dataBinding)
-{
-    _dataBinding = dataBinding;
-}
-
-}
+#include "Base.h"
+#include "Button.h"
+#include "Gamepad.h"
+
+namespace gameplay
+{
+
+Button::Button() : _dataBinding(0)
+{
+    _canFocus = true;
+}
+
+Button::~Button()
+{
+}
+
+Button* Button::create(const char* id, Theme::Style* style)
+{
+    Button* button = new Button();
+    button->_id = id ? id : "";
+    button->initialize("Button", style, NULL);
+    return button;
+}
+
+Control* Button::create(Theme::Style* style, Properties* properties)
+{
+    Button* button = new Button();
+	button->initialize("Button", style, properties);
+    return button;
+}
+
+void Button::initialize(const char* typeName, Theme::Style* style, Properties* properties)
+{
+    Label::initialize(typeName, style, properties);
+
+    if (properties)
+    {
+        // Different types of data bindings can be named differently in a button namespace.
+        // Gamepad button mappings have the name "mapping" and correspond to Gamepad::ButtonMapping enums.
+        const char* mapping = properties->getString("mapping");
+        if (mapping)
+        {
+            _dataBinding = Gamepad::getButtonMappingFromString(mapping);
+        }
+    }
+}
+
+const char* Button::getType() const
+{
+    return "button";
+}
+
+const unsigned int Button::getDataBinding() const
+{
+    return _dataBinding;
+}
+
+void Button::setDataBinding(unsigned int dataBinding)
+{
+    _dataBinding = dataBinding;
+}
+
+}

+ 96 - 135
gameplay/src/Button.h

@@ -1,135 +1,96 @@
-#ifndef BUTTON_H_
-#define BUTTON_H_
-
-#include "Container.h"
-#include "Label.h"
-#include "Touch.h"
-#include "Theme.h"
-#include "Properties.h"
-
-namespace gameplay
-{
-
-/**
- * Defines a button UI control. This is essentially a label that can have a callback method set on it.
- *
- * The following properties are available for buttons:
-
- @verbatim
-    button <buttonID>
-    {
-         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'
-         text        = <string>
-         consumeEvents = <bool>  // Whether the button propagates input events to the Game's input event handler. Default is true.
-    }
- @endverbatim
- */
-class Button : public Label
-{
-    friend class Container;
-    friend class Gamepad;
-
-public:
-
-    /**
-     * Create a new button control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new button.
-     * @script{create}
-     */
-    static Button* create(const char* id, Theme::Style* style);
-
-protected:
-
-    /**
-     * Constructor.
-     */
-    Button();
-
-    /**
-     * Destructor.
-     */
-    virtual ~Button();
-
-    /**
-     * Create a button with a given style and properties.
-     *
-     * @param style The style to apply to this button.
-     * @param properties The properties to set on this button.
-     *
-     * @return The new button.
-     */
-    static Button* create(Theme::Style* style, Properties* properties);
-
-    /**
-     * Touch callback on touch events.  Controls return true if they consume the touch event.
-     *
-     * @param evt The touch event that occurred.
-     * @param x The x position of the touch in pixels. Left edge is zero.
-     * @param y The y position of the touch in pixels. Top edge is zero.
-     * @param contactIndex The order of occurrence for multiple touch contacts starting at zero.
-     *
-     * @return Whether the touch event was consumed by the control.
-     *
-     * @see Touch::TouchEvent
-     */
-    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
-     */
-    const char* getType() const;
-
-    /**
-     * Gets the data binding index for this control.
-     *
-     * @return The data binding index for control. 
-     */
-    const unsigned int getDataBinding() const;
-
-    /**
-     * Sets the data binding provider for this control.
-     *
-     * @param dataBinding The data binding index for control. 
-     */
-    void setDataBinding(unsigned int dataBinding);
-
-private:
-
-    /**
-     * Constructor.
-     */
-    Button(const Button& copy);
-
-    unsigned int _dataBinding;
-
-};
-
-}
-
-#endif
+#ifndef BUTTON_H_
+#define BUTTON_H_
+
+#include "Container.h"
+#include "Label.h"
+#include "Touch.h"
+#include "Theme.h"
+#include "Properties.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a button control. 
+ *
+ * @see http://blackberry.github.io/GamePlay/docs/file-formats.html#wiki-UI_Forms
+ */
+class Button : public Label
+{
+    friend class Container;
+    friend class Gamepad;
+    friend class ControlFactory;
+
+public:
+
+    /**
+     * Creates a new Button.
+     *
+     * @param id The button ID.
+     * @param style The button style (optional).
+     *
+     * @return The new button.
+     * @script{create}
+     */
+    static Button* create(const char* id, Theme::Style* style = NULL);
+
+protected:
+
+    /**
+     * Constructor.
+     */
+    Button();
+
+    /**
+     * Destructor.
+     */
+    virtual ~Button();
+
+    /**
+     * Create a button with a given style and properties.
+     *
+     * @param style The style to apply to this button.
+     * @param properties A properties object containing a definition of the button (optional).
+     *
+     * @return The new button.
+     */
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
+
+    /**
+     * @see Control::initialize
+     */
+    void initialize(const char* typeName, Theme::Style* style, Properties* properties);
+
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
+    /**
+     * Gets the data binding index for this control.
+     *
+     * @return The data binding index for control. 
+     */
+    const unsigned int getDataBinding() const;
+
+    /**
+     * Sets the data binding provider for this control.
+     *
+     * @param dataBinding The data binding index for control. 
+     */
+    void setDataBinding(unsigned int dataBinding);
+
+private:
+
+    /**
+     * Constructor.
+     */
+    Button(const Button& copy);
+
+    unsigned int _dataBinding;
+
+};
+
+}
+
+#endif

+ 499 - 445
gameplay/src/Camera.cpp

@@ -1,445 +1,499 @@
-#include "Base.h"
-#include "Camera.h"
-#include "Game.h"
-#include "Node.h"
-#include "Game.h"
-#include "PhysicsController.h"
-
-// Camera dirty bits
-#define CAMERA_DIRTY_VIEW 1
-#define CAMERA_DIRTY_PROJ 2
-#define CAMERA_DIRTY_VIEW_PROJ 4
-#define CAMERA_DIRTY_INV_VIEW 8
-#define CAMERA_DIRTY_INV_VIEW_PROJ 16
-#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),
-      _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),
-      _bits(CAMERA_DIRTY_ALL), _node(NULL)
-{
-    // Orthographic camera.
-    _zoom[0] = zoomX;
-    _zoom[1] = zoomY;
-}
-
-Camera::~Camera()
-{
-}
-
-Camera* Camera::createPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
-{
-    return new Camera(fieldOfView, aspectRatio, nearPlane, farPlane);
-}
-
-Camera* Camera::createOrthographic(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane)
-{
-    return new Camera(zoomX, zoomY, aspectRatio, nearPlane, farPlane);
-}
-
-Camera* Camera::create(Properties* properties)
-{
-    GP_ASSERT(properties);
-
-    // Read camera type
-    std::string typeStr;
-    if (properties->exists("type"))
-        typeStr = properties->getString("type");
-    Camera::Type type;
-    if (typeStr == "PERSPECTIVE")
-    {
-        type = Camera::PERSPECTIVE;
-    }
-    else if (typeStr == "ORTHOGRAPHIC")
-    {
-        type = Camera::ORTHOGRAPHIC;
-    }
-    else
-    {
-        GP_ERROR("Invalid 'type' parameter for camera definition.");
-        return NULL;
-    }
-
-    // Read common parameters
-    float aspectRatio, nearPlane, farPlane;
-    if (properties->exists("aspectRatio"))
-    {
-        aspectRatio = properties->getFloat("aspectRatio");
-    }
-    else
-    {
-        // Use default aspect ratio
-        aspectRatio = (float)Game::getInstance()->getWidth() / Game::getInstance()->getHeight();
-    }
-
-    if (properties->exists("nearPlane"))
-        nearPlane = properties->getFloat("nearPlane");
-    else
-        nearPlane = 0.2f; // use some reasonable default value
-
-    if (properties->exists("farPlane"))
-        farPlane = properties->getFloat("farPlane");
-    else
-        farPlane = 100; // use some reasonable default value
-
-    Camera* camera = NULL;
-
-    switch (type)
-    {
-    case Camera::PERSPECTIVE:
-        // If field of view is not specified, use a default of 60 degrees
-        camera = createPerspective(
-            properties->exists("fieldOfView") ? properties->getFloat("fieldOfView") : 60.0f,
-            aspectRatio, nearPlane, farPlane);
-        break;
-
-    case Camera::ORTHOGRAPHIC:
-        // If zoomX and zoomY are not specified, use screen width/height
-        camera = createOrthographic(
-            properties->exists("zoomX") ? properties->getFloat("zoomX") : Game::getInstance()->getWidth(),
-            properties->exists("zoomY") ? properties->getFloat("zoomY") : Game::getInstance()->getHeight(),
-            aspectRatio, nearPlane, farPlane);
-        break;
-    }
-
-    return camera;
-}
-
-Camera::Type Camera::getCameraType() const
-{
-    return _type;
-}
-
-float Camera::getFieldOfView() const
-{
-    GP_ASSERT(_type == Camera::PERSPECTIVE);
-
-    return _fieldOfView;
-}
-
-void Camera::setFieldOfView(float fieldOfView)
-{
-    GP_ASSERT(_type == Camera::PERSPECTIVE);
-
-    _fieldOfView = fieldOfView;
-    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
-}
-
-float Camera::getZoomX() const
-{
-    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
-
-    return _zoom[0];
-}
-
-void Camera::setZoomX(float zoomX)
-{
-    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
-
-    _zoom[0] = zoomX;
-    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
-}
-
-float Camera::getZoomY() const
-{
-    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
-
-    return _zoom[1];
-}
-
-void Camera::setZoomY(float zoomY)
-{
-    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
-
-    _zoom[1] = zoomY;
-    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
-}
-
-float Camera::getAspectRatio() const
-{
-    return _aspectRatio;
-}
-
-void Camera::setAspectRatio(float aspectRatio)
-{
-    _aspectRatio = aspectRatio;
-    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
-}
-
-float Camera::getNearPlane() const
-{
-    return _nearPlane;
-}
-
-void Camera::setNearPlane(float nearPlane)
-{
-    _nearPlane = nearPlane;
-    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
-}
-
-float Camera::getFarPlane() const
-{
-    return _farPlane;
-}
-
-void Camera::setFarPlane(float farPlane)
-{
-    _farPlane = farPlane;
-    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
-}
-
-Node* Camera::getNode() const
-{
-    return _node;
-}
-
-void Camera::setNode(Node* node)
-{
-    if (_node != node)
-    {
-        if (_node)
-        {
-            _node->removeListener(this);
-        }
-
-        // Connect the new node.
-        _node = node;
-
-        if (_node)
-        {
-            _node->addListener(this);
-        }
-
-        _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 (_bits & CAMERA_DIRTY_VIEW)
-    {
-        if (_node)
-        {
-            // The view matrix is the inverse of our transform matrix.
-            _node->getWorldMatrix().invert(&_view);
-        }
-        else
-        {
-            _view.setIdentity();
-        }
-
-        _bits &= ~CAMERA_DIRTY_VIEW;
-    }
-
-    return _view;
-}
-
-const Matrix& Camera::getInverseViewMatrix() const
-{
-    if (_bits & CAMERA_DIRTY_INV_VIEW)
-    {
-        getViewMatrix().invert(&_inverseView);
-
-        _bits &= ~CAMERA_DIRTY_INV_VIEW;
-    }
-
-    return _inverseView;
-}
-
-const Matrix& Camera::getProjectionMatrix() const
-{
-    if (!(_bits & CAMERA_CUSTOM_PROJECTION) && (_bits & CAMERA_DIRTY_PROJ))
-    {
-        if (_type == PERSPECTIVE)
-        {
-            Matrix::createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane, &_projection);
-        }
-        else
-        {
-            Matrix::createOrthographic(_zoom[0], _zoom[1], _nearPlane, _farPlane, &_projection);
-        }
-
-        _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 (_bits & CAMERA_DIRTY_VIEW_PROJ)
-    {
-        Matrix::multiply(getProjectionMatrix(), getViewMatrix(), &_viewProjection);
-
-        _bits &= ~CAMERA_DIRTY_VIEW_PROJ;
-    }
-
-    return _viewProjection;
-}
-
-const Matrix& Camera::getInverseViewProjectionMatrix() const
-{
-    if (_bits & CAMERA_DIRTY_INV_VIEW_PROJ)
-    {
-        getViewProjectionMatrix().invert(&_inverseViewProjection);
-
-        _bits &= ~CAMERA_DIRTY_INV_VIEW_PROJ;
-    }
-
-    return _inverseViewProjection;
-}
-
-const Frustum& Camera::getFrustum() const
-{
-    if (_bits & CAMERA_DIRTY_BOUNDS)
-    {
-        // Update our bounding frustum from our view projection matrix.
-        _bounds.set(getViewProjectionMatrix());
-
-        _bits &= ~CAMERA_DIRTY_BOUNDS;
-    }
-
-    return _bounds;
-}
-
-void Camera::project(const Rectangle& viewport, const Vector3& position, float* x, float* y, float* depth) const
-{
-    GP_ASSERT(x);
-    GP_ASSERT(y);
-
-    // Transform the point to clip-space.
-    Vector4 clipPos;
-    getViewProjectionMatrix().transformVector(Vector4(position.x, position.y, position.z, 1.0f), &clipPos);
-
-    // Compute normalized device coordinates.
-    GP_ASSERT(clipPos.w != 0.0f);
-    float ndcX = clipPos.x / clipPos.w;
-    float ndcY = clipPos.y / clipPos.w;
-
-    // Compute screen coordinates by applying our viewport transformation.
-    *x = viewport.x + (ndcX + 1.0f) * 0.5f * viewport.width;
-    *y = viewport.y + (1.0f - (ndcY + 1.0f) * 0.5f) * viewport.height;
-    if (depth)
-    {
-        float ndcZ = clipPos.z / clipPos.w;
-        *depth = ndcZ + 1.0f / 2.0f;
-    }
-}
-
-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);
-    
-    // Create our screen space position in NDC.
-    GP_ASSERT(viewport.width != 0.0f && viewport.height != 0.0f);
-    Vector4 screen((x - viewport.x) / viewport.width, ((viewport.height - y) - viewport.y) / viewport.height, depth, 1.0f);
-
-    // Map to range -1 to 1.
-    screen.x = screen.x * 2.0f - 1.0f;
-    screen.y = screen.y * 2.0f - 1.0f;
-    screen.z = screen.z * 2.0f - 1.0f;
-
-    // Transform the screen-space NDC by our inverse view projection matrix.
-    getInverseViewProjectionMatrix().transformVector(screen, &screen);
-
-    // Divide by our W coordinate.
-    if (screen.w != 0.0f)
-    {
-        screen.x /= screen.w;
-        screen.y /= screen.w;
-        screen.z /= screen.w;
-    }
-
-    dst->set(screen.x, screen.y, screen.z);
-}
-
-void Camera::pickRay(const Rectangle& viewport, float x, float y, Ray* dst) const
-{
-    GP_ASSERT(dst);
-
-    // Get the world-space position at the near clip plane.
-    Vector3 nearPoint;
-    unproject(viewport, x, y, 0.0f, &nearPoint);
-
-    // Get the world-space position at the far clip plane.
-    Vector3 farPoint;
-    unproject(viewport, x, y, 1.0f, &farPoint);
-
-    // Set the direction of the ray.
-    Vector3 direction;
-    Vector3::subtract(farPoint, nearPoint, &direction);
-    direction.normalize();
-
-    dst->set(nearPoint, direction);
-}
-
-Camera* Camera::clone(NodeCloneContext &context) const
-{
-    Camera* cameraClone = NULL;
-    if (getCameraType() == PERSPECTIVE)
-    {
-        cameraClone = createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane);
-    }
-    else if (getCameraType() == ORTHOGRAPHIC)
-    {
-        cameraClone = createOrthographic(getZoomX(), getZoomY(), getAspectRatio(), _nearPlane, _farPlane);
-    }
-    GP_ASSERT(cameraClone);
-
-    if (Node* node = context.findClonedNode(getNode()))
-    {
-        cameraClone->setNode(node);
-    }
-    return cameraClone;
-}
-
-void Camera::transformChanged(Transform* transform, long cookie)
-{
-    _bits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
-}
-
-}
+#include "Base.h"
+#include "Camera.h"
+#include "Game.h"
+#include "Node.h"
+#include "Game.h"
+#include "PhysicsController.h"
+
+// Camera dirty bits
+#define CAMERA_DIRTY_VIEW 1
+#define CAMERA_DIRTY_PROJ 2
+#define CAMERA_DIRTY_VIEW_PROJ 4
+#define CAMERA_DIRTY_INV_VIEW 8
+#define CAMERA_DIRTY_INV_VIEW_PROJ 16
+#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),
+    _bits(CAMERA_DIRTY_ALL), _node(NULL), _listeners(NULL)
+{
+}
+
+Camera::Camera(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane)
+    : _type(ORTHOGRAPHIC), _aspectRatio(aspectRatio), _nearPlane(nearPlane), _farPlane(farPlane),
+	_bits(CAMERA_DIRTY_ALL), _node(NULL), _listeners(NULL)
+{
+    // Orthographic camera.
+    _zoom[0] = zoomX;
+    _zoom[1] = zoomY;
+}
+
+Camera::~Camera()
+{
+    SAFE_DELETE(_listeners);
+}
+
+Camera* Camera::createPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
+{
+    return new Camera(fieldOfView, aspectRatio, nearPlane, farPlane);
+}
+
+Camera* Camera::createOrthographic(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane)
+{
+    return new Camera(zoomX, zoomY, aspectRatio, nearPlane, farPlane);
+}
+
+Camera* Camera::create(Properties* properties)
+{
+    GP_ASSERT(properties);
+
+    // Read camera type
+    std::string typeStr;
+    if (properties->exists("type"))
+        typeStr = properties->getString("type");
+    Camera::Type type;
+    if (typeStr == "PERSPECTIVE")
+    {
+        type = Camera::PERSPECTIVE;
+    }
+    else if (typeStr == "ORTHOGRAPHIC")
+    {
+        type = Camera::ORTHOGRAPHIC;
+    }
+    else
+    {
+        GP_ERROR("Invalid 'type' parameter for camera definition.");
+        return NULL;
+    }
+
+    // Read common parameters
+    float aspectRatio, nearPlane, farPlane;
+    if (properties->exists("aspectRatio"))
+    {
+        aspectRatio = properties->getFloat("aspectRatio");
+    }
+    else
+    {
+        // Use default aspect ratio
+        aspectRatio = (float)Game::getInstance()->getWidth() / Game::getInstance()->getHeight();
+    }
+
+    if (properties->exists("nearPlane"))
+        nearPlane = properties->getFloat("nearPlane");
+    else
+        nearPlane = 0.2f; // use some reasonable default value
+
+    if (properties->exists("farPlane"))
+        farPlane = properties->getFloat("farPlane");
+    else
+        farPlane = 100; // use some reasonable default value
+
+    Camera* camera = NULL;
+
+    switch (type)
+    {
+    case Camera::PERSPECTIVE:
+        // If field of view is not specified, use a default of 60 degrees
+        camera = createPerspective(
+            properties->exists("fieldOfView") ? properties->getFloat("fieldOfView") : 60.0f,
+            aspectRatio, nearPlane, farPlane);
+        break;
+
+    case Camera::ORTHOGRAPHIC:
+        // If zoomX and zoomY are not specified, use screen width/height
+        camera = createOrthographic(
+            properties->exists("zoomX") ? properties->getFloat("zoomX") : Game::getInstance()->getWidth(),
+            properties->exists("zoomY") ? properties->getFloat("zoomY") : Game::getInstance()->getHeight(),
+            aspectRatio, nearPlane, farPlane);
+        break;
+    }
+
+    return camera;
+}
+
+Camera::Type Camera::getCameraType() const
+{
+    return _type;
+}
+
+float Camera::getFieldOfView() const
+{
+    GP_ASSERT(_type == Camera::PERSPECTIVE);
+
+    return _fieldOfView;
+}
+
+void Camera::setFieldOfView(float fieldOfView)
+{
+    GP_ASSERT(_type == Camera::PERSPECTIVE);
+
+    _fieldOfView = fieldOfView;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    cameraChanged();
+}
+
+float Camera::getZoomX() const
+{
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
+
+    return _zoom[0];
+}
+
+void Camera::setZoomX(float zoomX)
+{
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
+
+    _zoom[0] = zoomX;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    cameraChanged();
+}
+
+float Camera::getZoomY() const
+{
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
+
+    return _zoom[1];
+}
+
+void Camera::setZoomY(float zoomY)
+{
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
+
+    _zoom[1] = zoomY;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    cameraChanged();
+}
+
+float Camera::getAspectRatio() const
+{
+    return _aspectRatio;
+}
+
+void Camera::setAspectRatio(float aspectRatio)
+{
+    _aspectRatio = aspectRatio;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    cameraChanged();
+}
+
+float Camera::getNearPlane() const
+{
+    return _nearPlane;
+}
+
+void Camera::setNearPlane(float nearPlane)
+{
+    _nearPlane = nearPlane;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    cameraChanged();
+}
+
+float Camera::getFarPlane() const
+{
+    return _farPlane;
+}
+
+void Camera::setFarPlane(float farPlane)
+{
+    _farPlane = farPlane;
+    _bits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+    cameraChanged();
+}
+
+Node* Camera::getNode() const
+{
+    return _node;
+}
+
+void Camera::setNode(Node* node)
+{
+    if (_node != node)
+    {
+        if (_node)
+        {
+            _node->removeListener(this);
+        }
+
+        // Connect the new node.
+        _node = node;
+
+        if (_node)
+        {
+            _node->addListener(this);
+        }
+
+        _bits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+        cameraChanged();
+    }
+}
+
+const Matrix& Camera::getViewMatrix() const
+{
+    if (_bits & CAMERA_DIRTY_VIEW)
+    {
+        if (_node)
+        {
+            // The view matrix is the inverse of our transform matrix.
+            _node->getWorldMatrix().invert(&_view);
+        }
+        else
+        {
+            _view.setIdentity();
+        }
+
+        _bits &= ~CAMERA_DIRTY_VIEW;
+    }
+
+    return _view;
+}
+
+const Matrix& Camera::getInverseViewMatrix() const
+{
+    if (_bits & CAMERA_DIRTY_INV_VIEW)
+    {
+        getViewMatrix().invert(&_inverseView);
+
+        _bits &= ~CAMERA_DIRTY_INV_VIEW;
+    }
+
+    return _inverseView;
+}
+
+const Matrix& Camera::getProjectionMatrix() const
+{
+    if (!(_bits & CAMERA_CUSTOM_PROJECTION) && (_bits & CAMERA_DIRTY_PROJ))
+    {
+        if (_type == PERSPECTIVE)
+        {
+            Matrix::createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane, &_projection);
+        }
+        else
+        {
+            // Create an ortho projection with the origin at the bottom left of the viewport, +X to the right and +Y up.
+            Matrix::createOrthographic(_zoom[0], _zoom[1], _nearPlane, _farPlane, &_projection);
+        }
+
+        _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;
+
+    cameraChanged();
+}
+
+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;
+
+        cameraChanged();
+    }
+}
+
+const Matrix& Camera::getViewProjectionMatrix() const
+{
+    if (_bits & CAMERA_DIRTY_VIEW_PROJ)
+    {
+        Matrix::multiply(getProjectionMatrix(), getViewMatrix(), &_viewProjection);
+
+        _bits &= ~CAMERA_DIRTY_VIEW_PROJ;
+    }
+
+    return _viewProjection;
+}
+
+const Matrix& Camera::getInverseViewProjectionMatrix() const
+{
+    if (_bits & CAMERA_DIRTY_INV_VIEW_PROJ)
+    {
+        getViewProjectionMatrix().invert(&_inverseViewProjection);
+
+        _bits &= ~CAMERA_DIRTY_INV_VIEW_PROJ;
+    }
+
+    return _inverseViewProjection;
+}
+
+const Frustum& Camera::getFrustum() const
+{
+    if (_bits & CAMERA_DIRTY_BOUNDS)
+    {
+        // Update our bounding frustum from our view projection matrix.
+        _bounds.set(getViewProjectionMatrix());
+
+        _bits &= ~CAMERA_DIRTY_BOUNDS;
+    }
+
+    return _bounds;
+}
+
+void Camera::project(const Rectangle& viewport, const Vector3& position, float* x, float* y, float* depth) const
+{
+    GP_ASSERT(x);
+    GP_ASSERT(y);
+
+    // Transform the point to clip-space.
+    Vector4 clipPos;
+    getViewProjectionMatrix().transformVector(Vector4(position.x, position.y, position.z, 1.0f), &clipPos);
+
+    // Compute normalized device coordinates.
+    GP_ASSERT(clipPos.w != 0.0f);
+    float ndcX = clipPos.x / clipPos.w;
+    float ndcY = clipPos.y / clipPos.w;
+
+    // Compute screen coordinates by applying our viewport transformation.
+    *x = viewport.x + (ndcX + 1.0f) * 0.5f * viewport.width;
+    *y = viewport.y + (1.0f - (ndcY + 1.0f) * 0.5f) * viewport.height;
+    if (depth)
+    {
+        float ndcZ = clipPos.z / clipPos.w;
+        *depth = (ndcZ + 1.0f) / 2.0f;
+    }
+}
+
+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);
+    
+    // Create our screen space position in NDC.
+    GP_ASSERT(viewport.width != 0.0f && viewport.height != 0.0f);
+    Vector4 screen((x - viewport.x) / viewport.width, ((viewport.height - y) - viewport.y) / viewport.height, depth, 1.0f);
+
+    // Map to range -1 to 1.
+    screen.x = screen.x * 2.0f - 1.0f;
+    screen.y = screen.y * 2.0f - 1.0f;
+    screen.z = screen.z * 2.0f - 1.0f;
+
+    // Transform the screen-space NDC by our inverse view projection matrix.
+    getInverseViewProjectionMatrix().transformVector(screen, &screen);
+
+    // Divide by our W coordinate.
+    if (screen.w != 0.0f)
+    {
+        screen.x /= screen.w;
+        screen.y /= screen.w;
+        screen.z /= screen.w;
+    }
+
+    dst->set(screen.x, screen.y, screen.z);
+}
+
+void Camera::pickRay(const Rectangle& viewport, float x, float y, Ray* dst) const
+{
+    GP_ASSERT(dst);
+
+    // Get the world-space position at the near clip plane.
+    Vector3 nearPoint;
+    unproject(viewport, x, y, 0.0f, &nearPoint);
+
+    // Get the world-space position at the far clip plane.
+    Vector3 farPoint;
+    unproject(viewport, x, y, 1.0f, &farPoint);
+
+    // Set the direction of the ray.
+    Vector3 direction;
+    Vector3::subtract(farPoint, nearPoint, &direction);
+    direction.normalize();
+
+    dst->set(nearPoint, direction);
+}
+
+Camera* Camera::clone(NodeCloneContext &context) const
+{
+    Camera* cameraClone = NULL;
+    if (getCameraType() == PERSPECTIVE)
+    {
+        cameraClone = createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane);
+    }
+    else if (getCameraType() == ORTHOGRAPHIC)
+    {
+        cameraClone = createOrthographic(getZoomX(), getZoomY(), getAspectRatio(), _nearPlane, _farPlane);
+    }
+    GP_ASSERT(cameraClone);
+
+    if (Node* node = context.findClonedNode(getNode()))
+    {
+        cameraClone->setNode(node);
+    }
+    return cameraClone;
+}
+
+void Camera::transformChanged(Transform* transform, long cookie)
+{
+    _bits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
+
+    cameraChanged();
+}
+
+void Camera::cameraChanged()
+{
+    if (_listeners == NULL)
+        return;
+
+    for (std::list<Camera::Listener*>::iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
+    {
+        Camera::Listener* listener = (*itr);
+        listener->cameraChanged(this);
+    }
+}
+
+void Camera::addListener(Camera::Listener* listener)
+{
+    GP_ASSERT(listener);
+
+    if (_listeners == NULL)
+        _listeners = new std::list<Camera::Listener*>();
+
+    _listeners->push_back(listener);
+}
+
+void Camera::removeListener(Camera::Listener* listener)
+{
+    GP_ASSERT(listener);
+
+    if (_listeners)
+    {
+        for (std::list<Camera::Listener*>::iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
+        {
+            if ((*itr) == listener)
+            {
+                _listeners->erase(itr);
+                break;
+            }
+        }
+    }
+}
+
+}

+ 385 - 348
gameplay/src/Camera.h

@@ -1,348 +1,385 @@
-#ifndef CAMERA_H_
-#define CAMERA_H_
-
-#include "Ref.h"
-#include "Transform.h"
-#include "Frustum.h"
-#include "Rectangle.h"
-#include "Properties.h"
-
-namespace gameplay
-{
-
-class Node;
-class NodeCloneContext;
-
-/**
- * Defines a camera which acts as a view of a scene to be rendered.
- */
-class Camera : public Ref, public Transform::Listener
-{
-    friend class Node;
-
-public:
-
-    /**
-     * The type of camera.
-     */
-    enum Type
-    {
-        PERSPECTIVE = 1,
-        ORTHOGRAPHIC = 2
-    };
-
-    /**
-     * Creates a perspective camera.
-     *
-     * @param fieldOfView The field of view for the perspective camera (normally in the range of 40-60 degrees).
-     * @param aspectRatio The aspect ratio of the camera (normally the width of the viewport divided by the height of the viewport).
-     * @param nearPlane The near plane distance.
-     * @param farPlane The far plane distance.
-     *
-     * @return The new Camera.
-     */
-    static Camera* createPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane);
-
-    /**
-     * Creates an orthographic camera.
-     *
-     * @param zoomX The zoom factor along the X-axis of the orthographic projection (the width of the ortho projection).
-     * @param zoomY The zoom factor along the Y-axis of the orthographic projection (the height of the ortho projection).
-     * @param aspectRatio The aspect ratio of the orthographic projection.
-     * @param nearPlane The near plane distance.
-     * @param farPlane The far plane distance.
-     *
-     * @return The new Camera.
-     */
-    static Camera* createOrthographic(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane);
-
-    /**
-     * Creates a camera from a properties definition.
-     *
-     * The properties object must contain a "type" parameter, specifying either PERSPECTIVE or ORTHOGRAPHIC,
-     * as well as values for all required parameters in the Camera::createPerspective and Camera::createOrthographic
-     * methods.
-     *
-     * @param properties The properties definition of the Camera.
-     *
-     * @return The new Camera.
-     */
-    static Camera* create(Properties* properties);
-
-    /**
-     * Gets the type of camera.
-     *
-     * @return The camera type.
-     */
-    Camera::Type getCameraType() const;
-
-    /**
-     * Gets the field of view for a perspective camera.
-     *
-     * @return The field of view.
-     */
-    float getFieldOfView() const;
-
-    /**
-     * Sets the field of view.
-     *
-     * @param fieldOfView The field of view.
-     */
-    void setFieldOfView(float fieldOfView);
-
-    /**
-     * Gets the x-zoom (magnification) for an orthographic camera.
-     * Default is 1.0f.
-     *
-     * @return The magnification (zoom) for x.
-     */
-    float getZoomX() const;
-
-    /**
-     * Sets the x-zoom (magnification) for a orthographic camera.
-     * Default is 1.0f.
-     *
-     * @param zoomX The magnification (zoom) for x.
-     */
-    void setZoomX(float zoomX);
-
-    /**
-     * Gets the y-zoom (magnification) for a orthographic camera.
-     * Default is 1.0f.
-     *
-     * @return The magnification (zoom) for y.
-     */
-    float getZoomY() const;
-
-    /**
-     * Sets the y-zoom (magnification) for a orthographic camera.
-     *
-     * @param zoomY The magnification (zoom) for y.
-     */
-    void setZoomY(float zoomY);
-
-    /**
-     * Gets the aspect ratio.
-     *
-     * @return The aspect ratio.
-     */
-    float getAspectRatio() const;
-
-    /**
-     * Sets the aspect ratio.
-     *
-     * @param aspectRatio The aspect ratio.
-     */
-    void setAspectRatio(float aspectRatio);
-
-    /**
-     * Gets the near z clipping plane distance.
-     *
-     * @return The near z clipping plane distance.
-     */
-    float getNearPlane() const;
-
-    /**
-     * Sets the near z clipping plane distance.
-     *
-     * @param nearPlane The near z clipping plane distance.
-     */
-    void setNearPlane(float nearPlane);
-
-    /**
-     * Gets the far z clipping plane distance.
-     *
-     * @return The far z clipping plane distance.
-     */
-    float getFarPlane() const;
-
-    /**
-     * Sets the far z clipping plane distance.
-     *
-     * @param farPlane The far z clipping plane distance.
-     */
-    void setFarPlane(float farPlane);
-
-    /**
-     * Gets the node that this camera is attached to.
-     *
-     * @return The node that this camera is attached to.
-     */
-    Node* getNode() const;
-
-    /**
-     * Gets the camera's view matrix.
-     *
-     * @return The camera view matrix.
-     */
-    const Matrix& getViewMatrix() const;
-
-    /**
-     * Gets the camera's inverse view matrix.
-     *
-     * @return The camera inverse view matrix.
-     */
-    const Matrix& getInverseViewMatrix() const;
-
-    /**
-     * Gets the camera's projection matrix.
-     *
-     * @return The camera projection matrix.
-     */
-    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.
-     *
-     * @return The camera view * projection matrix.
-     */
-    const Matrix& getViewProjectionMatrix() const;
-
-    /**
-     * Gets the camera's inverse view * projection matrix.
-     *
-     * @return The camera inverse view * projection matrix.
-     */
-    const Matrix& getInverseViewProjectionMatrix() const;
-
-    /**
-     * Gets the view bounding frustum.
-     *
-     * @return The viewing bounding frustum.
-     */
-    const Frustum& getFrustum() const;
-
-    /**
-     * Projects the specified world position into the viewport coordinates.
-     *
-     * @param viewport The viewport rectangle to use.
-     * @param position The world space position.
-     * @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.
-     *
-     * The depth parameter is a value ranging between 0 and 1, where 0 returns a point on the
-     * near clipping plane and 1 returns a point on the far clipping plane.
-     *
-     * @param viewport The viewport rectangle to use.
-     * @param x The viewport-space x coordinate.
-     * @param y The viewport-space y coordinate.
-     * @param depth The depth range.
-     * @param dst The world space position.
-     */
-    void unproject(const Rectangle& viewport, float x, float y, float depth, Vector3* dst) const;
-
-    /**
-     * Picks a ray that can be used for picking given the specified viewport-space coordinates.
-     *
-     * @param viewport The viewport rectangle to use.
-     * @param x The viewport x-coordinate.
-     * @param y The viewport y-coordinate.
-     * @param dst The computed pick ray.
-     */
-    void pickRay(const Rectangle& viewport, float x, float y, Ray* dst) const;
-
-private:
-
-    /**
-     * Constructor.
-     */
-    Camera(float fieldOfView, float aspectRatio, float nearPlane, float farPlane);
-
-    /**
-     * Constructor.
-     */
-    Camera(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane);
-
-    /**
-     * Destructor.
-     */
-    virtual ~Camera();
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    Camera& operator=(const Camera&);
-
-    /**
-     * Clones the camera and returns a new camera.
-     * 
-     * @param context The clone context.
-     * @return The newly created camera.
-     */
-    Camera* clone(NodeCloneContext &context) const;
-
-    /**
-     * @see Transform::Listener::transformChanged
-     */
-    void transformChanged(Transform* transform, long cookie);
-
-    /**
-     * Sets the node associated with this camera.
-     */
-    void setNode(Node* node);
-
-    Camera::Type _type;
-    float _fieldOfView;
-    float _zoom[2];
-    float _aspectRatio;
-    float _nearPlane;
-    float _farPlane;
-    mutable Matrix _view;
-    mutable Matrix _projection;
-    mutable Matrix _viewProjection;
-    mutable Matrix _inverseView;
-    mutable Matrix _inverseViewProjection;
-    mutable Frustum _bounds;
-    mutable int _bits;
-    Node* _node;
-};
-
-}
-
-#endif
+#ifndef CAMERA_H_
+#define CAMERA_H_
+
+#include "Ref.h"
+#include "Transform.h"
+#include "Frustum.h"
+#include "Rectangle.h"
+#include "Properties.h"
+
+namespace gameplay
+{
+
+class Node;
+class NodeCloneContext;
+
+/**
+ * Defines a camera which acts as a view of a scene to be rendered.
+ */
+class Camera : public Ref, public Transform::Listener
+{
+    friend class Node;
+
+public:
+
+    /**
+     * The type of camera.
+     */
+    enum Type
+    {
+        PERSPECTIVE = 1,
+        ORTHOGRAPHIC = 2
+    };
+
+    /**
+     * Listener interface for camera events.
+     */
+    class Listener
+    {
+    public:
+
+        virtual ~Listener() { }
+
+        /**
+         * Handles when an camera settings change or the transform changed for the node its attached to.
+         *
+         * @param camera The camera that was changed.
+         */
+        virtual void cameraChanged(Camera* camera) = 0;
+    };
+
+    /**
+     * Creates a perspective camera.
+     *
+     * @param fieldOfView The field of view for the perspective camera (normally in the range of 40-60 degrees).
+     * @param aspectRatio The aspect ratio of the camera (normally the width of the viewport divided by the height of the viewport).
+     * @param nearPlane The near plane distance.
+     * @param farPlane The far plane distance.
+     *
+     * @return The new Camera.
+     */
+    static Camera* createPerspective(float fieldOfView, float aspectRatio, float nearPlane, float farPlane);
+
+    /**
+     * Creates an orthographic camera.
+     *
+     * @param zoomX The zoom factor along the X-axis of the orthographic projection (the width of the ortho projection).
+     * @param zoomY The zoom factor along the Y-axis of the orthographic projection (the height of the ortho projection).
+     * @param aspectRatio The aspect ratio of the orthographic projection.
+     * @param nearPlane The near plane distance.
+     * @param farPlane The far plane distance.
+     *
+     * @return The new Camera.
+     */
+    static Camera* createOrthographic(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane);
+
+    /**
+     * Creates a camera from a properties definition.
+     *
+     * The properties object must contain a "type" parameter, specifying either PERSPECTIVE or ORTHOGRAPHIC,
+     * as well as values for all required parameters in the Camera::createPerspective and Camera::createOrthographic
+     * methods.
+     *
+     * @param properties The properties definition of the Camera.
+     *
+     * @return The new Camera.
+     */
+    static Camera* create(Properties* properties);
+
+    /**
+     * Gets the type of camera.
+     *
+     * @return The camera type.
+     */
+    Camera::Type getCameraType() const;
+
+    /**
+     * Gets the field of view for a perspective camera.
+     *
+     * @return The field of view.
+     */
+    float getFieldOfView() const;
+
+    /**
+     * Sets the field of view.
+     *
+     * @param fieldOfView The field of view.
+     */
+    void setFieldOfView(float fieldOfView);
+
+    /**
+     * Gets the x-zoom (magnification) for an orthographic camera.
+     * Default is 1.0f.
+     *
+     * @return The magnification (zoom) for x.
+     */
+    float getZoomX() const;
+
+    /**
+     * Sets the x-zoom (magnification) for a orthographic camera.
+     * Default is 1.0f.
+     *
+     * @param zoomX The magnification (zoom) for x.
+     */
+    void setZoomX(float zoomX);
+
+    /**
+     * Gets the y-zoom (magnification) for a orthographic camera.
+     * Default is 1.0f.
+     *
+     * @return The magnification (zoom) for y.
+     */
+    float getZoomY() const;
+
+    /**
+     * Sets the y-zoom (magnification) for a orthographic camera.
+     *
+     * @param zoomY The magnification (zoom) for y.
+     */
+    void setZoomY(float zoomY);
+
+    /**
+     * Gets the aspect ratio.
+     *
+     * @return The aspect ratio.
+     */
+    float getAspectRatio() const;
+
+    /**
+     * Sets the aspect ratio.
+     *
+     * @param aspectRatio The aspect ratio.
+     */
+    void setAspectRatio(float aspectRatio);
+
+    /**
+     * Gets the near z clipping plane distance.
+     *
+     * @return The near z clipping plane distance.
+     */
+    float getNearPlane() const;
+
+    /**
+     * Sets the near z clipping plane distance.
+     *
+     * @param nearPlane The near z clipping plane distance.
+     */
+    void setNearPlane(float nearPlane);
+
+    /**
+     * Gets the far z clipping plane distance.
+     *
+     * @return The far z clipping plane distance.
+     */
+    float getFarPlane() const;
+
+    /**
+     * Sets the far z clipping plane distance.
+     *
+     * @param farPlane The far z clipping plane distance.
+     */
+    void setFarPlane(float farPlane);
+
+    /**
+     * Gets the node that this camera is attached to.
+     *
+     * @return The node that this camera is attached to.
+     */
+    Node* getNode() const;
+
+    /**
+     * Gets the camera's view matrix.
+     *
+     * @return The camera view matrix.
+     */
+    const Matrix& getViewMatrix() const;
+
+    /**
+     * Gets the camera's inverse view matrix.
+     *
+     * @return The camera inverse view matrix.
+     */
+    const Matrix& getInverseViewMatrix() const;
+
+    /**
+     * Gets the camera's projection matrix.
+     *
+     * @return The camera projection matrix.
+     */
+    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 overridden until
+     * the resetProjectionMatrix method is called. A custom projection
+     * matrix is normally not necessary, 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.
+     *
+     * @return The camera view * projection matrix.
+     */
+    const Matrix& getViewProjectionMatrix() const;
+
+    /**
+     * Gets the camera's inverse view * projection matrix.
+     *
+     * @return The camera inverse view * projection matrix.
+     */
+    const Matrix& getInverseViewProjectionMatrix() const;
+
+    /**
+     * Gets the view bounding frustum.
+     *
+     * @return The viewing bounding frustum.
+     */
+    const Frustum& getFrustum() const;
+
+    /**
+     * Projects the specified world position into the viewport coordinates.
+     *
+     * @param viewport The viewport rectangle to use.
+     * @param position The world space position.
+     * @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.
+     *
+     * The depth parameter is a value ranging between 0 and 1, where 0 returns a point on the
+     * near clipping plane and 1 returns a point on the far clipping plane.
+     *
+     * @param viewport The viewport rectangle to use.
+     * @param x The viewport-space x coordinate.
+     * @param y The viewport-space y coordinate.
+     * @param depth The depth range.
+     * @param dst The world space position.
+     */
+    void unproject(const Rectangle& viewport, float x, float y, float depth, Vector3* dst) const;
+
+    /**
+     * Picks a ray that can be used for picking given the specified viewport-space coordinates.
+     *
+     * @param viewport The viewport rectangle to use.
+     * @param x The viewport x-coordinate.
+     * @param y The viewport y-coordinate.
+     * @param dst The computed pick ray.
+     */
+    void pickRay(const Rectangle& viewport, float x, float y, Ray* dst) const;
+
+    /**
+    * Adds a camera listener.
+    *
+    * @param listener The listener to add.
+    */
+    void addListener(Camera::Listener* listener);
+
+    /**
+     * Removes a camera listener.
+     *
+     * @param listener The listener to remove.
+     */
+    void removeListener(Camera::Listener* listener);
+
+private:
+
+    /**
+     * Constructor.
+     */
+    Camera(float fieldOfView, float aspectRatio, float nearPlane, float farPlane);
+
+    /**
+     * Constructor.
+     */
+    Camera(float zoomX, float zoomY, float aspectRatio, float nearPlane, float farPlane);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Camera();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Camera& operator=(const Camera&);
+
+    /**
+     * Clones the camera and returns a new camera.
+     *
+     * @param context The clone context.
+     * @return The newly created camera.
+     */
+    Camera* clone(NodeCloneContext &context) const;
+
+    /**
+     * Sets the node associated with this camera.
+     */
+    void setNode(Node* node);
+
+    /**
+     * @see Transform::Listener::transformChanged
+     */
+    void transformChanged(Transform* transform, long cookie);
+
+    /**
+     *
+     */
+    void cameraChanged();
+
+    Camera::Type _type;
+    float _fieldOfView;
+    float _zoom[2];
+    float _aspectRatio;
+    float _nearPlane;
+    float _farPlane;
+    mutable Matrix _view;
+    mutable Matrix _projection;
+    mutable Matrix _viewProjection;
+    mutable Matrix _inverseView;
+    mutable Matrix _inverseViewProjection;
+    mutable Frustum _bounds;
+    mutable int _bits;
+    Node* _node;
+    std::list<Camera::Listener*>* _listeners;
+};
+
+}
+
+#endif

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor