Browse Source

Merge branch 'next' of https://github.com/blackberry-gaming/GamePlay into next-sgrenier

Conflicts:
	gameplay-samples/sample03-character/res/scene.gpb
	gameplay/src/SceneLoader.cpp
	gameplay/src/SceneLoader.h
Steve Grenier 13 years ago
parent
commit
1d245dfd07
74 changed files with 3272 additions and 2153 deletions
  1. 55 10
      gameplay-encoder/src/FBXSceneEncoder.cpp
  2. 1 1
      gameplay/android/jni/Android.mk
  3. 0 4
      gameplay/gameplay.vcxproj
  4. 0 12
      gameplay/gameplay.vcxproj.filters
  5. 3 2
      gameplay/src/AnimationTarget.cpp
  6. 3 3
      gameplay/src/AnimationTarget.h
  7. 1 1
      gameplay/src/AudioController.cpp
  8. 3 2
      gameplay/src/AudioSource.cpp
  9. 2 1
      gameplay/src/AudioSource.h
  10. 1 1
      gameplay/src/Base.h
  11. 12 34
      gameplay/src/Camera.cpp
  12. 9 8
      gameplay/src/Camera.h
  13. 67 40
      gameplay/src/CheckBox.cpp
  14. 7 5
      gameplay/src/CheckBox.h
  15. 0 38
      gameplay/src/CloneContext.cpp
  16. 0 86
      gameplay/src/CloneContext.h
  17. 4 14
      gameplay/src/Container.cpp
  18. 1 1
      gameplay/src/Container.h
  19. 609 72
      gameplay/src/Control.cpp
  20. 408 14
      gameplay/src/Control.h
  21. 148 1
      gameplay/src/DebugNew.cpp
  22. 14 0
      gameplay/src/DebugNew.h
  23. 7 4
      gameplay/src/Font.h
  24. 14 14
      gameplay/src/Form.cpp
  25. 6 3
      gameplay/src/Form.h
  26. 14 5
      gameplay/src/Game.cpp
  27. 32 1
      gameplay/src/Game.h
  28. 5 0
      gameplay/src/Game.inl
  29. 1 1
      gameplay/src/Joint.cpp
  30. 1 1
      gameplay/src/Joint.h
  31. 4 1
      gameplay/src/Label.cpp
  32. 1 1
      gameplay/src/Light.cpp
  33. 2 2
      gameplay/src/Light.h
  34. 2 1
      gameplay/src/Material.cpp
  35. 3 1
      gameplay/src/Material.h
  36. 1 1
      gameplay/src/Model.cpp
  37. 2 1
      gameplay/src/Model.h
  38. 68 9
      gameplay/src/Node.cpp
  39. 76 3
      gameplay/src/Node.h
  40. 2 1
      gameplay/src/Pass.cpp
  41. 2 1
      gameplay/src/Pass.h
  42. 25 0
      gameplay/src/PhysicsCharacter.cpp
  43. 12 0
      gameplay/src/PhysicsCharacter.h
  44. 408 160
      gameplay/src/PhysicsCollisionShape.cpp
  45. 235 219
      gameplay/src/PhysicsCollisionShape.h
  46. 31 6
      gameplay/src/PhysicsGhostObject.cpp
  47. 12 2
      gameplay/src/PhysicsGhostObject.h
  48. 73 235
      gameplay/src/PhysicsRigidBody.cpp
  49. 0 10
      gameplay/src/PhysicsRigidBody.h
  50. 56 37
      gameplay/src/RadioButton.cpp
  51. 4 4
      gameplay/src/RadioButton.h
  52. 14 14
      gameplay/src/Rectangle.cpp
  53. 14 14
      gameplay/src/Rectangle.h
  54. 8 0
      gameplay/src/Ref.cpp
  55. 2 0
      gameplay/src/Ref.h
  56. 2 1
      gameplay/src/RenderState.cpp
  57. 2 2
      gameplay/src/RenderState.h
  58. 0 10
      gameplay/src/Scene.cpp
  59. 0 15
      gameplay/src/Scene.h
  60. 58 28
      gameplay/src/SceneLoader.cpp
  61. 8 6
      gameplay/src/SceneLoader.h
  62. 49 58
      gameplay/src/Slider.cpp
  63. 1 1
      gameplay/src/Slider.h
  64. 2 1
      gameplay/src/Technique.cpp
  65. 2 1
      gameplay/src/Technique.h
  66. 9 44
      gameplay/src/TextBox.cpp
  67. 1 1
      gameplay/src/TextBox.h
  68. 505 421
      gameplay/src/Theme.cpp
  69. 137 237
      gameplay/src/Theme.h
  70. 2 1
      gameplay/src/Transform.cpp
  71. 2 1
      gameplay/src/Transform.h
  72. 7 5
      gameplay/src/VerticalLayout.cpp
  73. 0 86
      gameplay/src/Viewport.cpp
  74. 0 132
      gameplay/src/Viewport.h

+ 55 - 10
gameplay-encoder/src/FBXSceneEncoder.cpp

@@ -249,13 +249,13 @@ void FBXSceneEncoder::loadScene(KFbxScene* fbxScene)
     // Find the ambient light of the scene
     KFbxColor ambientColor = fbxScene->GetGlobalSettings().GetAmbientColor();
     scene->setAmbientColor((float)ambientColor.mRed, (float)ambientColor.mGreen, (float)ambientColor.mBlue);
-
-	// Assign the first camera node (if there is one) in the scene as the active camera
-	// This ensures that if there's a camera in the scene that it is assigned as the 
-	// active camera.
-	// TODO: add logic to find the "active" camera node in the fbxScene
-	scene->setActiveCameraNode(scene->getFirstCameraNode());
-
+    
+    // Assign the first camera node (if there is one) in the scene as the active camera
+    // This ensures that if there's a camera in the scene that it is assigned as the 
+    // active camera.
+    // TODO: add logic to find the "active" camera node in the fbxScene
+    scene->setActiveCameraNode(scene->getFirstCameraNode());
+    
     _gamePlayFile.addScene(scene);
 }
 
@@ -652,20 +652,65 @@ void FBXSceneEncoder::loadLight(KFbxNode* fbxNode, Node* node)
     switch (fbxLight->LightType.Get())
     {
     case KFbxLight::ePOINT:
-        light->setPointLight();
-        // TODO: range
+    {
+        KFbxLight::EDecayType decayType = fbxLight->DecayType.Get();
+        switch (decayType)
+        {
+        case KFbxLight::eNONE:
+            // No decay. Can assume we have an ambient light, because ambient lights in the scene are 
+            // converted to point lights with no decay when exporting to FBX.
+            light->setAmbientLight();
+            break;
+        case KFbxLight::eLINEAR:
+            light->setPointLight();
+            light->setLinearAttenuation((float)fbxLight->DecayStart.Get());
+            break;
+        case KFbxLight::eQUADRATIC:
+            light->setPointLight();
+            light->setQuadraticAttenuation((float)fbxLight->DecayStart.Get());
+            break;
+        case KFbxLight::eCUBIC:
+        default:
+            // Not supported..
+            break;
+        }
         break;
+    }
     case KFbxLight::eDIRECTIONAL:
+    {
         light->setDirectionalLight();
         break;
+    }
     case KFbxLight::eSPOT:
+    {
         light->setSpotLight();
-        // TODO: range and angles
+
+        KFbxLight::EDecayType decayType = fbxLight->DecayType.Get();
+        switch (decayType)
+        {
+        case KFbxLight::eNONE:
+            // No decay.
+            break;
+        case KFbxLight::eLINEAR:
+            light->setLinearAttenuation((float)fbxLight->DecayStart.Get());
+            break;  
+        case KFbxLight::eQUADRATIC:
+            light->setQuadraticAttenuation((float)fbxLight->DecayStart.Get());
+            break;
+        case KFbxLight::eCUBIC:
+            // Not supported..
+            break;
+        }
+
+        light->setFalloffAngle(MATH_DEG_TO_RAD((float)fbxLight->ConeAngle.Get())); // fall off angle
         break;
+    }
     default:
+    {
         warning("Unknown light type in node.");
         return;
     }
+    }
 
     _gamePlayFile.addLight(light);
     node->setLight(light);

+ 1 - 1
gameplay/android/jni/Android.mk

@@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir)/../../src
 
 include $(CLEAR_VARS)
 LOCAL_MODULE    := libgameplay
-LOCAL_SRC_FILES := Animation.cpp DepthStencilTarget.cpp MeshBatch.cpp PhysicsRigidBody.cpp SceneLoader.cpp AnimationClip.cpp Effect.cpp MeshPart.cpp PhysicsSocketConstraint.cpp SpriteBatch.cpp AnimationController.cpp FileSystem.cpp MeshSkin.cpp PhysicsSpringConstraint.cpp Technique.cpp AnimationTarget.cpp Font.cpp Model.cpp Plane.cpp Texture.cpp AnimationValue.cpp FrameBuffer.cpp Node.cpp PlatformAndroid.cpp PlatformQNX.cpp AudioBuffer.cpp Frustum.cpp Package.cpp PlatformWin32.cpp Transform.cpp AudioController.cpp Game.cpp ParticleEmitter.cpp Properties.cpp Vector2.cpp AudioListener.cpp Image.cpp Pass.cpp Quaternion.cpp Vector3.cpp AudioSource.cpp Joint.cpp PhysicsConstraint.cpp Ray.cpp Vector4.cpp BoundingBox.cpp Light.cpp PhysicsController.cpp Rectangle.cpp VertexAttributeBinding.cpp BoundingSphere.cpp Material.cpp PhysicsFixedConstraint.cpp Ref.cpp VertexFormat.cpp Camera.cpp MaterialParameter.cpp PhysicsGenericConstraint.cpp RenderState.cpp Viewport.cpp Curve.cpp Matrix.cpp PhysicsHingeConstraint.cpp RenderTarget.cpp DebugNew.cpp Mesh.cpp PhysicsMotionState.cpp Scene.cpp
+LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp gameplay-main-qnx.cpp gameplay-main-win32.cpp Image.cpp Joint.cpp Label.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp Package.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp PlatformQNX.cpp PlatformWin32.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
 LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include"
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 

+ 0 - 4
gameplay/gameplay.vcxproj

@@ -30,7 +30,6 @@
     <ClCompile Include="src\Button.cpp" />
     <ClCompile Include="src\Camera.cpp" />
     <ClCompile Include="src\CheckBox.cpp" />
-    <ClCompile Include="src\CloneContext.cpp" />
     <ClCompile Include="src\Container.cpp" />
     <ClCompile Include="src\Control.cpp" />
     <ClCompile Include="src\Curve.cpp" />
@@ -102,7 +101,6 @@
     <ClCompile Include="src\VertexAttributeBinding.cpp" />
     <ClCompile Include="src\VertexFormat.cpp" />
     <ClCompile Include="src\VerticalLayout.cpp" />
-    <ClCompile Include="src\Viewport.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\AbsoluteLayout.h" />
@@ -121,7 +119,6 @@
     <ClInclude Include="src\Button.h" />
     <ClInclude Include="src\Camera.h" />
     <ClInclude Include="src\CheckBox.h" />
-    <ClInclude Include="src\CloneContext.h" />
     <ClInclude Include="src\Container.h" />
     <ClInclude Include="src\Control.h" />
     <ClInclude Include="src\Curve.h" />
@@ -194,7 +191,6 @@
     <ClInclude Include="src\VertexAttributeBinding.h" />
     <ClInclude Include="src\VertexFormat.h" />
     <ClInclude Include="src\VerticalLayout.h" />
-    <ClInclude Include="src\Viewport.h" />
   </ItemGroup>
   <ItemGroup>
     <None Include="res\logo_black.png" />

+ 0 - 12
gameplay/gameplay.vcxproj.filters

@@ -126,9 +126,6 @@
     <ClCompile Include="src\VertexFormat.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Viewport.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
     <ClCompile Include="src\AudioController.cpp">
       <Filter>src</Filter>
     </ClCompile>
@@ -276,9 +273,6 @@
     <ClCompile Include="src\PhysicsCollisionShape.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\CloneContext.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -398,9 +392,6 @@
     <ClInclude Include="src\VertexFormat.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Viewport.h">
-      <Filter>src</Filter>
-    </ClInclude>
     <ClInclude Include="src\AudioController.h">
       <Filter>src</Filter>
     </ClInclude>
@@ -548,9 +539,6 @@
     <ClInclude Include="src\PhysicsCollisionShape.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\CloneContext.h">
-      <Filter>src</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 3 - 2
gameplay/src/AnimationTarget.cpp

@@ -2,6 +2,7 @@
 #include "AnimationTarget.h"
 #include "Animation.h"
 #include "Game.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -118,7 +119,7 @@ void AnimationTarget::deleteChannel(Animation::Channel* channel)
     }
 }
 
-void AnimationTarget::cloneInto(AnimationTarget* target, CloneContext &context) const
+void AnimationTarget::cloneInto(AnimationTarget* target, NodeCloneContext &context) const
 {
     if (_animationChannels)
     {
@@ -129,7 +130,7 @@ void AnimationTarget::cloneInto(AnimationTarget* target, CloneContext &context)
 
             bool animationCloned = false;
 
-            // Don't clone the Animaton if it is already in the CloneContext.
+            // Don't clone the Animaton if it is already in the clone context.
             Animation* animation = context.findClonedAnimation(channel->_animation);
             if (animation == NULL)
             {

+ 3 - 3
gameplay/src/AnimationTarget.h

@@ -3,13 +3,13 @@
 
 #include "Curve.h"
 #include "AnimationController.h"
-#include "CloneContext.h"
 
 namespace gameplay
 {
 
 class Animation;
 class AnimationValue;
+class NodeCloneContext;
 
 /**
  * Defines an interface allowing animation to target
@@ -87,11 +87,11 @@ protected:
      * @param target The target to copy into.
      * @param context The clone context.
      */
-    void cloneInto(AnimationTarget* target, CloneContext &context) const;
+    void cloneInto(AnimationTarget* target, NodeCloneContext &context) const;
 
     TargetType _targetType;             // The type of target this is.
 
-    char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.
+    unsigned char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.
 
 private:
 

+ 1 - 1
gameplay/src/AudioController.cpp

@@ -181,7 +181,7 @@ void AudioController::update(long elapsedTime)
             SLresult result = (*_engineEngine)->CreateListener(_engineEngine, &_listenerObject, 2, interfaces, required);
             if (result != SL_RESULT_SUCCESS)
             {
-                WARN("AudioController: failed to create listener.");
+                WARN_VARG("AudioController: failed to create listener (%u).", result);
                 return;
             }
 

+ 3 - 2
gameplay/src/AudioSource.cpp

@@ -4,6 +4,7 @@
 #include "AudioController.h"
 #include "AudioSource.h"
 #include "Game.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -484,7 +485,7 @@ void AudioSource::transformChanged(Transform* transform, long cookie)
 #endif
 }
 
-AudioSource* AudioSource::clone(CloneContext &context) const
+AudioSource* AudioSource::clone(NodeCloneContext &context) const
 {
 #ifndef __ANDROID__
     ALuint alSource = 0;
@@ -497,7 +498,7 @@ AudioSource* AudioSource::clone(CloneContext &context) const
     AudioSource* audioClone = new AudioSource(_buffer, alSource);
 #else
     // TODO: Implement cloning audio source for Android
-    AudioSource* audioClone = new AudioSource(AudioBuffer* buffer, const SLObjectItf& player);
+    AudioSource* audioClone = new AudioSource(_buffer, _playerObject);
 
 #endif
     _buffer->addRef();

+ 2 - 1
gameplay/src/AudioSource.h

@@ -10,6 +10,7 @@ namespace gameplay
 
 class AudioBuffer;
 class Node;
+class NodeCloneContext;
 
 /**
  *  Declares an audio source in 3D space.
@@ -183,7 +184,7 @@ private:
      * 
      * @return The newly created audio source.
      */
-    AudioSource* clone(CloneContext &context) const;
+    AudioSource* clone(NodeCloneContext &context) const;
 
 #ifndef __ANDROID__
     ALuint _alSource;

+ 1 - 1
gameplay/src/Base.h

@@ -143,7 +143,7 @@ extern void printError(const char* format, ...);
 #define MATH_LOG2E                  1.442695040888963387f
 #define MATH_PI                     3.14159265358979323846f
 #define MATH_PIOVER2                1.57079632679489661923f
-#define MATH_PIOVER4                M_PI_4
+#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))

+ 12 - 34
gameplay/src/Camera.cpp

@@ -243,24 +243,13 @@ const Frustum& Camera::getFrustum() const
     return _bounds;
 }
 
-void Camera::project(const Viewport* viewport, const Vector3& position, float* x, float* y, float* depth)
+void Camera::project(const Rectangle& viewport, const Vector3& position, float* x, float* y, float* depth)
 {
     // Determine viewport coords to use.
-    float vpx, vpy, vpw, vph;
-    if (viewport)
-    {
-        vpx = viewport->getX();
-        vpy = viewport->getY();
-        vpw = viewport->getWidth();
-        vph = viewport->getHeight();
-    }
-    else
-    {
-        vpx = 0;
-        vpy = 0;
-        vpw = Game::getInstance()->getWidth();
-        vph = Game::getInstance()->getHeight();
-    }
+    float vpx = viewport.x;
+    float vpy = viewport.y;
+    float vpw = viewport.width;
+    float vph = viewport.height;
 
     // Transform the point to clip-space.
     Vector4 clipPos;
@@ -280,24 +269,13 @@ void Camera::project(const Viewport* viewport, const Vector3& position, float* x
     }
 }
 
-void Camera::unproject(const Viewport* viewport, float x, float y, float depth, Vector3* dst)
+void Camera::unproject(const Rectangle& viewport, float x, float y, float depth, Vector3* dst)
 {
     // Determine viewport coords to use.
-    float vpx, vpy, vpw, vph;
-    if (viewport)
-    {
-        vpx = viewport->getX();
-        vpy = viewport->getY();
-        vpw = viewport->getWidth();
-        vph = viewport->getHeight();
-    }
-    else
-    {
-        vpx = 0;
-        vpy = 0;
-        vpw = Game::getInstance()->getWidth();
-        vph = Game::getInstance()->getHeight();
-    }
+    float vpx = viewport.x;
+    float vpy = viewport.y;
+    float vpw = viewport.width;
+    float vph = viewport.height;
     
     // Create our screen space position in NDC.
     Vector4 screen(
@@ -325,7 +303,7 @@ void Camera::unproject(const Viewport* viewport, float x, float y, float depth,
     dst->set(screen.x, screen.y, screen.z);
 }
 
-void Camera::pickRay(const Viewport* viewport, float x, float y, Ray* dst)
+void Camera::pickRay(const Rectangle& viewport, float x, float y, Ray* dst)
 {
     // Get the world-space position at the near clip plane.
     Vector3 nearPoint;
@@ -343,7 +321,7 @@ void Camera::pickRay(const Viewport* viewport, float x, float y, Ray* dst)
     dst->set(nearPoint, direction);
 }
 
-Camera* Camera::clone(CloneContext &context) const
+Camera* Camera::clone(NodeCloneContext &context) const
 {
     Camera* cameraClone = NULL;
     if (getCameraType() == PERSPECTIVE)

+ 9 - 8
gameplay/src/Camera.h

@@ -4,12 +4,13 @@
 #include "Ref.h"
 #include "Transform.h"
 #include "Frustum.h"
-#include "Viewport.h"
+#include "Rectangle.h"
 
 namespace gameplay
 {
 
 class Node;
+class NodeCloneContext;
 
 /**
  * Defines a camera which acts as a view of a scene to be rendered.
@@ -196,13 +197,13 @@ public:
     /**
      * Projects the specified world position into the viewport coordinates.
      *
-     * @param viewport The viewport to use, or NULL to use a viewport the size of the window.
+     * @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).
      */
-    void project(const Viewport* viewport, const Vector3& position, float* x, float* y, float* depth = NULL);
+    void project(const Rectangle& viewport, const Vector3& position, float* x, float* y, float* depth = NULL);
 
     /**
      * Converts a viewport-space coordinate to a world-space position for the given depth value.
@@ -210,23 +211,23 @@ public:
      * 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 to use, or NULL to use a viewport the size of the window.
+     * @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 Viewport* viewport, float x, float y, float depth, Vector3* dst);
+    void unproject(const Rectangle& viewport, float x, float y, float depth, Vector3* dst);
 
     /**
      * Picks a ray that can be used for picking given the specified viewport-space coordinates.
      *
-     * @param viewport The viewport to use, or NULL to use a viewport the size of the window.
+     * @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 Viewport* viewport, float x, float y, Ray* dst);
+    void pickRay(const Rectangle& viewport, float x, float y, Ray* dst);
 
 private:
 
@@ -252,7 +253,7 @@ private:
      * 
      * @return The newly created camera.
      */
-    Camera* clone(CloneContext &context) const;
+    Camera* clone(NodeCloneContext &context) const;
 
     /**
      * @see Transform::Listener::transformChanged

+ 67 - 40
gameplay/src/CheckBox.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "CheckBox.h"
+#include "Game.h"
 
 namespace gameplay
 {
@@ -22,7 +23,7 @@ CheckBox* CheckBox::create(Theme::Style* style, Properties* properties)
 {
     CheckBox* checkBox = new CheckBox();
     checkBox->init(style, properties);
-    properties->getVector2("iconSize", &checkBox->_iconSize);
+    properties->getVector2("iconSize", &checkBox->_imageSize);
     checkBox->_checked = properties->getBool("checked");
 
     return checkBox;
@@ -33,29 +34,22 @@ bool CheckBox::isChecked()
     return _checked;
 }
 
-void CheckBox::setIconSize(float width, float height)
+void CheckBox::setImageSize(float width, float height)
 {
-    _iconSize.set(width, height);
+    _imageSize.set(width, height);
 }
 
-const Vector2& CheckBox::getIconSize() const
+const Vector2& CheckBox::getImageSize() const
 {
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    if (_iconSize.isZero() && icon)
-    {
-        return icon->getSize();
-    }
-
-    return _iconSize;
+    return _imageSize;
 }
 
 void CheckBox::addListener(Control::Listener* listener, int eventFlags)
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
         assert("TEXT_CHANGED event is not applicable to CheckBox.");
-        eventFlags &= ~Listener::TEXT_CHANGED;
+        eventFlags &= ~Control::Listener::TEXT_CHANGED;
     }
 
     Control::addListener(listener, eventFlags);
@@ -78,7 +72,15 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
                     y > 0 && y <= _bounds.height)
                 {
                     _checked = !_checked;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
+
+                    // Animate between icons.  Old fades out, then the new fades in.
+                    /*
+                    AnimationController* animationController = Game::getInstance()->getAnimationController();
+                    float from[1] = { 1.0f };
+                    float to[1] = { 0.0f };
+                    animationController->createAnimationFromTo("CheckBox::toggle", this, CheckBox::ANIMATE_SPRITE_ALPHA, from, to, Curve::QUADRATIC_IN_OUT, 200L);
+                    */
                 }
             }
         }
@@ -92,12 +94,23 @@ void CheckBox::update(const Rectangle& clip)
 {
     Control::update(clip);
 
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    Vector2& size = _iconSize;
-    if (_iconSize.isZero() && icon)
+    Vector2 size;
+    if (_imageSize.isZero())
+    {
+        if (_checked)
+        {
+            const Rectangle& selectedRegion = getImageRegion("checked", _state);
+            size.set(selectedRegion.width, selectedRegion.height);
+        }
+        else
+        {
+            const Rectangle& unselectedRegion = getImageRegion("unchecked", _state);
+            size.set(unselectedRegion.width, unselectedRegion.height);
+        }
+    }
+    else
     {
-        size = icon->getSize();
+        size.set(_imageSize);
     }
     float iconWidth = size.x;
 
@@ -105,43 +118,57 @@ void CheckBox::update(const Rectangle& clip)
     _textBounds.width -= iconWidth;
 }
 
-void CheckBox::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // Left, v-center.
     // TODO: Set an alignment for icons.
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    if (icon)
+    const Theme::Border border = getBorder(_state);
+    const Theme::Padding padding = _style->getPadding();
+    float opacity = getOpacity(_state);
+
+    if (_checked)
     {
-        const Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        Theme::Border border;
-        if (containerRegion)
+        const Rectangle& selectedRegion = getImageRegion("checked", _state);
+        const Theme::UVs& selected = getImageUVs("checked", _state);
+        Vector4 selectedColor = getImageColor("checked", _state);
+        selectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-                border = containerRegion->getBorder();
+            size.set(selectedRegion.width, selectedRegion.height);
         }
-        const Theme::Padding padding = _style->getPadding();
-
-        Vector2& size = _iconSize;
-        if (_iconSize.isZero())
+        else
         {
-            size = icon->getSize();
+            size.set(_imageSize);
         }
 
-        const Vector4 color = icon->getColor();
-
         Vector2 pos(clip.x + _position.x + border.left + padding.left,
             clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
-        if (_checked)
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
+    }
+    else
+    {
+        const Rectangle& unselectedRegion = getImageRegion("unchecked", _state);
+        const Theme::UVs& unselected = getImageUVs("unchecked", _state);
+        Vector4 unselectedColor = getImageColor("unchecked", _state);
+        unselectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-            const Theme::UVs on = icon->getOnUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, on.u1, on.v1, on.u2, on.v2, color, _clip);
+            size.set(unselectedRegion.width, unselectedRegion.height);
         }
         else
         {
-            const Theme::UVs off = icon->getOffUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, off.u1, off.v1, off.u2, off.v2, color, _clip);
+            size.set(_imageSize);
         }
+
+        Vector2 pos(clip.x + _position.x + border.left + padding.left,
+            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
     }
 }
 

+ 7 - 5
gameplay/src/CheckBox.h

@@ -24,7 +24,7 @@ namespace gameplay
  *      iconSize    = <width, height>   // The size to draw the checkbox icon, if different from its size in the texture.
  * }
  */
-class CheckBox : public Button
+class CheckBox : public Button //, public AnimationClip::Listener
 {
     friend class Container;
 
@@ -42,14 +42,14 @@ public:
      * @param width The width to draw the checkbox icon.
      * @param height The height to draw the checkbox icon.
      */
-    void setIconSize(float width, float height);
+    void setImageSize(float width, float height);
 
     /**
      * Get the size at which the checkbox icon will be drawn.
      *
      * @return The size of the checkbox icon.
      */
-    const Vector2& getIconSize() const;
+    const Vector2& getImageSize() const;
 
     /**
      * Add a listener to be notified of specific events affecting
@@ -63,6 +63,8 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+  //  virtual void animationEvent(AnimationClip* clip, EventType type);
+
 protected:
     CheckBox();
     ~CheckBox();
@@ -105,10 +107,10 @@ protected:
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param position The container position this control is relative to.
      */
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     bool _checked;      // Whether this checkbox is currently checked.
-    Vector2 _iconSize;  // The size to draw the checkbox icon, if different from its size in the texture.
+    Vector2 _imageSize;  // The size to draw the checkbox icon, if different from its size in the texture.
 
 private:
     CheckBox(const CheckBox& copy);

+ 0 - 38
gameplay/src/CloneContext.cpp

@@ -1,38 +0,0 @@
-#include "CloneContext.h"
-
-namespace gameplay
-{
-
-CloneContext::CloneContext()
-{
-    
-}
-
-CloneContext::~CloneContext()
-{
-
-}
-
-Animation* CloneContext::findClonedAnimation(const Animation* animation)
-{
-    AnimationMap::iterator it = _clonedAnimations.find(animation);
-    return it != _clonedAnimations.end() ? it->second : NULL;
-}
-
-void CloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
-{
-    _clonedAnimations[original] = clone;
-}
-
-Node* CloneContext::findClonedNode(const Node* node)
-{
-    NodeMap::iterator it = _clonedNodes.find(node);
-    return it != _clonedNodes.end() ? it->second : NULL;
-}
-
-void CloneContext::registerClonedNode(const Node* original, Node* clone)
-{
-    _clonedNodes[original] = clone;
-}
-
-}

+ 0 - 86
gameplay/src/CloneContext.h

@@ -1,86 +0,0 @@
-#ifndef CLONECONTEXT_H_
-#define CLONECONTEXT_H_
-
-#include <map>
-
-namespace gameplay
-{
-    class Animation;
-    class Node;
-
-/**
- * CloneContext represents the context data that is kept when cloning a node.
- * 
- * The CloneContext is used to make sure objects don't get cloned twice.
- */
-class CloneContext
-{
-public:
-
-    /**
-     * Constructor.
-     */
-    CloneContext();
-
-    /**
-     * Destructor.
-     */
-    ~CloneContext();
-
-    /**
-     * Finds the cloned animation of the given animation or NULL if this animation was not registered with this context.
-     * 
-     * @param animation The animation to search for the cloned copy of.
-     * 
-     * @return The cloned animation or NULL if not found.
-     */
-    Animation* findClonedAnimation(const Animation* animation);
-
-    /**
-     * Registers the cloned animation with this context so that it doesn't get cloned twice.
-     * 
-     * @param original The pointer to the original animation.
-     * @param clone The pointer to the cloned animation.
-     */
-    void registerClonedAnimation(const Animation* original, Animation* clone);
-
-    /**
-     * Finds the cloned node of the given node or NULL if this node was not registered with this context.
-     * 
-     * @param node The node to search for the cloned copy of.
-     * 
-     * @return The cloned node or NULL if not found.
-     */
-    Node* findClonedNode(const Node* node);
-
-    /**
-     * Registers the cloned node with this context so that it doens't get cloned twice.
-     * 
-     * @param original The pointer to the original node.
-     * @param clone The pointer to the cloned node.
-     */
-    void registerClonedNode(const Node* original, Node* clone);
-
-private:
-    
-    /**
-     * Hidden copy constructor.
-     */
-    CloneContext(const CloneContext&);
-
-    /**
-     * Hidden copy assignment operator.
-     */
-    CloneContext& operator=(const CloneContext&);
-
-private:
-    typedef std::map<const Animation*, Animation*> AnimationMap;
-    typedef std::map<const Node*, Node*> NodeMap;
-
-    AnimationMap _clonedAnimations;
-    NodeMap _clonedNodes;
-};
-
-}
-
-#endif

+ 4 - 14
gameplay/src/Container.cpp

@@ -219,8 +219,6 @@ namespace gameplay
         Control::drawBorder(spriteBatch, clip);
 
         // Now call drawBorder on all controls within this container.
-        //Vector2 pos(clip.x + _position.x, clip.y + _position.y);
-        //const Rectangle newClip(clip.x + _position.x, clip.y + _position.y, _size.x, _size.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
@@ -229,14 +227,13 @@ namespace gameplay
         }
     }
 
-    void Container::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+    void Container::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     {
-        //const Rectangle newClip(clip.x + _position.x, clip.y + _position.y, _size.x, _size.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
             Control* control = *it;
-            control->drawSprites(spriteBatch, _clip);
+            control->drawImages(spriteBatch, _clip);
         }
 
         _dirty = false;
@@ -244,7 +241,6 @@ namespace gameplay
 
     void Container::drawText(const Rectangle& clip)
     {
-        //const Rectangle newClip(clip.x + _position.x, clip.y + _position.y, _size.x, _size.y);
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
@@ -285,14 +281,8 @@ namespace gameplay
 
         bool eventConsumed = false;
 
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
-        {
-            border = overlay->getContainerRegion()->getBorder();
-        }
-        Theme::Padding padding = _style->getPadding();
+        const Theme::Border& border = getBorder(_state);
+        const Theme::Padding& padding = getPadding();
         float xPos = border.left + padding.left;
         float yPos = border.top + padding.top;
 

+ 1 - 1
gameplay/src/Container.h

@@ -146,7 +146,7 @@ protected:
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param clip The clipping rectangle of this container's parent container.
      */
-    virtual void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     /**
      * Draws the text of all controls within this container.

+ 609 - 72
gameplay/src/Control.cpp

@@ -1,11 +1,12 @@
 #include "Base.h"
+#include "Game.h"
 #include "Control.h"
 
 namespace gameplay
 {
     Control::Control()
         : _id(""), _state(Control::NORMAL), _position(Vector2::zero()), _size(Vector2::zero()), _bounds(Rectangle::empty()), _clip(Rectangle::empty()),
-          _autoWidth(true), _autoHeight(true), _dirty(true), _consumeTouchEvents(true), _listeners(NULL)
+            _autoWidth(true), _autoHeight(true), _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
     {
     }
 
@@ -24,6 +25,11 @@ namespace gameplay
             }
             SAFE_DELETE(_listeners);
         }
+
+        if (_styleOverridden)
+        {
+            SAFE_DELETE(_style);
+        }
     }
 
     void Control::init(Theme::Style* style, Properties* properties)
@@ -37,9 +43,7 @@ namespace gameplay
 
         const char* id = properties->getId();
         if (id)
-        {
             _id = id;
-        }
     }
 
     const char* Control::getID() const
@@ -47,9 +51,24 @@ namespace gameplay
         return _id.c_str();
     }
 
-    void Control::setPosition(float x, float y)
+    void Control::setPosition(float x, float y, unsigned long duration)
     {
-        _position.set(x, y);
+        if (duration > 0L)
+        {
+            AnimationController* animationController = Game::getInstance()->getAnimationController();
+            float from[2] = { _position.x, _position.y };
+            float to[2] = { x, y };
+            Animation* moveAnimation = animationController->createAnimationFromTo("Control::setPosition", this, Control::ANIMATE_POSITION,
+                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration);
+            AnimationClip* clip = moveAnimation->getClip();
+            clip->play();
+        }
+        else
+        {
+            _position.set(x, y);
+        }
+
+        _dirty = true;
     }
 
     const Vector2& Control::getPosition() const
@@ -57,9 +76,24 @@ namespace gameplay
         return _position;
     }
 
-    void Control::setSize(float width, float height)
+    void Control::setSize(float width, float height, unsigned long duration)
     {
-        _size.set(width, height);
+        if (duration > 0L)
+        {
+            AnimationController* animationController = Game::getInstance()->getAnimationController();
+            float from[2] = { _size.x, _size.y };
+            float to[2] = { width, height };
+            Animation* resizeAnimation = animationController->createAnimationFromTo("Control::setSize", this, Control::ANIMATE_SIZE,
+                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration);
+            AnimationClip* clip = resizeAnimation->getClip();
+            clip->play();
+        }
+        else
+        {
+            _size.set(width, height);
+        }
+
+        _dirty = true;
     }
 
     const Vector2& Control::getSize() const
@@ -67,6 +101,312 @@ namespace gameplay
         return _size;
     }
 
+    void Control::setOpacity(float opacity, unsigned char states, unsigned long duration)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            if (duration > 0L)
+            {
+                float from[1] = { overlays[i]->getOpacity() };
+                float to[1] = { opacity };
+
+                // Fun with chaining.
+                Game::getInstance()->getAnimationController()->createAnimationFromTo("Overlay::setOpacity", overlays[i], Theme::Style::Overlay::ANIMATE_OPACITY,
+                    from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration)->getClip()->play();
+            }
+            else
+            {
+                overlays[i]->setOpacity(opacity);
+            }
+        }
+        
+        if (duration > 0L)
+        {
+            // All this animation does is make sure this control sets its dirty flag during the animation.
+            float from[1] = { 0.0f };
+            float to[1] = { 1.0f };
+
+            Game::getInstance()->getAnimationController()->createAnimationFromTo("Control::setOpacity", this, Control::ANIMATE_OPACITY,
+                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration)->getClip()->play();
+        }
+        
+        _dirty = true;
+    }
+
+    float Control::getOpacity(State state) const
+    {
+        return getOverlay(state)->getOpacity();
+    }
+
+    void Control::setBorder(float top, float bottom, float left, float right, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setBorder(top, bottom, left, right);
+        }
+
+        _dirty = true;
+    }
+
+    const Theme::Border& Control::getBorder(State state) const
+    {
+        return getOverlay(state)->getBorder();
+    }
+
+    void Control::setSkinRegion(const Rectangle& region, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setSkinRegion(region, _style->_tw, _style->_th);
+        }
+
+        _dirty = true;
+    }
+
+    const Rectangle& Control::getSkinRegion(State state) const
+    {
+        return getOverlay(state)->getSkinRegion();
+    }
+
+    const Theme::UVs& Control::getSkinUVs(Theme::Skin::SkinArea area, State state) const
+    {
+        return getOverlay(state)->getSkinUVs(area);
+    }
+
+    void Control::setSkinColor(const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setSkinColor(color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getSkinColor(State state) const
+    {
+        return getOverlay(state)->getSkinColor();
+    }
+
+    void Control::setMargin(float top, float bottom, float left, float right)
+    {
+        _style->setMargin(top, bottom, left, right);
+        _dirty = true;
+    }
+
+    const Theme::Margin& Control::getMargin() const
+    {
+        return _style->getMargin();
+    }
+
+    void Control::setPadding(float top, float bottom, float left, float right)
+    {
+        _style->setPadding(top, bottom, left, right);
+        _dirty = true;
+    }
+    
+    const Theme::Padding& Control::getPadding() const
+    {
+        return _style->getPadding();
+    }
+
+    void Control::setImageRegion(const char* id, const Rectangle& region, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setImageRegion(id, region, _style->_tw, _style->_th);
+        }
+
+        _dirty = true;
+    }
+
+    const Rectangle& Control::getImageRegion(const char* id, State state) const
+    {
+        return getOverlay(state)->getImageRegion(id);
+    }
+
+    void Control::setImageColor(const char* id, const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setImageColor(id, color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getImageColor(const char* id, State state) const
+    {
+        return getOverlay(state)->getImageColor(id);
+    }
+
+    const Theme::UVs& Control::getImageUVs(const char* id, State state) const
+    {
+        return getOverlay(state)->getImageUVs(id);
+    }
+
+    void Control::setCursorRegion(const Rectangle& region, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setCursorRegion(region, _style->_tw, _style->_th);
+        }
+
+        _dirty = true;
+    }
+
+    const Rectangle& Control::getCursorRegion(State state) const
+    {
+        return getOverlay(state)->getCursorRegion();
+    }
+
+    void Control::setCursorColor(const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setCursorColor(color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getCursorColor(State state)
+    {
+        return getOverlay(state)->getCursorColor();
+    }
+    
+    const Theme::UVs& Control::getCursorUVs(State state)
+    {
+        return getOverlay(state)->getCursorUVs();
+    }
+
+    void Control::setFont(Font* font, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setFont(font);
+        }
+
+        _dirty = true;
+    }
+
+    Font* Control::getFont(State state) const
+    {
+        return getOverlay(state)->getFont();
+    }
+
+    void Control::setFontSize(unsigned int fontSize, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setFontSize(fontSize);
+        }
+
+        _dirty = true;
+    }
+
+    unsigned int Control::getFontSize(State state) const
+    {
+        return getOverlay(state)->getFontSize();
+    }
+
+    void Control::setTextColor(const Vector4& color, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setTextColor(color);
+        }
+
+        _dirty = true;
+    }
+
+    const Vector4& Control::getTextColor(State state) const
+    {
+        return getOverlay(state)->getTextColor();
+    }
+
+    void Control::setTextAlignment(Font::Justify alignment, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setTextAlignment(alignment);
+        }
+
+        _dirty = true;
+    }
+
+    Font::Justify Control::getTextAlignment(State state) const
+    {
+        return getOverlay(state)->getTextAlignment();
+    }
+
+    void Control::setTextRightToLeft(bool rightToLeft, unsigned char states)
+    {
+        overrideStyle();
+        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        getOverlays(states, overlays);
+
+        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        {
+            overlays[i]->setTextRightToLeft(rightToLeft);
+        }
+
+        _dirty = true;
+    }
+
+    bool Control::getTextRightToLeft(State state) const
+    {
+        return getOverlay(state)->getTextRightToLeft();
+    }
+
     const Rectangle& Control::getBounds() const
     {
         return _bounds;
@@ -81,6 +421,7 @@ namespace gameplay
     {
         _autoWidth = width;
         _autoHeight = height;
+        _dirty = true;
     }
 
     void Control::setStyle(Theme::Style* style)
@@ -101,9 +442,10 @@ namespace gameplay
     void Control::setState(State state)
     {
         _state = state;
+        _dirty = true;
     }
 
-    Control::State Control::getState()
+    Control::State Control::getState() const
     {
         return _state;
     }
@@ -111,11 +453,13 @@ namespace gameplay
     void Control::disable()
     {
         _state = DISABLED;
+        _dirty = true;
     }
 
     void Control::enable()
     {
         _state = NORMAL;
+        _dirty = true;
     }
 
     bool Control::isEnabled()
@@ -270,14 +614,8 @@ namespace gameplay
         _bounds.set(_position.x, _position.y, width, height);
 
         // Calculate the clipping viewport.
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
-        {
-            border = overlay->getContainerRegion()->getBorder();
-        }
-        Theme::Padding padding = _style->getPadding();
+        const Theme::Border& border = getBorder(_state);
+        const Theme::Padding& padding = getPadding();
 
         x +=  border.left + padding.left;
         y +=  border.top + padding.top;
@@ -318,65 +656,61 @@ namespace gameplay
         Vector2 pos(clip.x + _position.x, clip.y + _position.y);
 
         // Get the border and background images for this control's current state.
-        Theme::ContainerRegion* containerRegion = _style->getOverlay(getOverlayType())->getContainerRegion();
-        if (containerRegion)
-        {
-            // Get the UVs.
-            Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
-            topLeft = containerRegion->getUVs(Theme::ContainerRegion::TOP_LEFT);
-            top = containerRegion->getUVs(Theme::ContainerRegion::TOP);
-            topRight = containerRegion->getUVs(Theme::ContainerRegion::TOP_RIGHT);
-            left = containerRegion->getUVs(Theme::ContainerRegion::LEFT);
-            center = containerRegion->getUVs(Theme::ContainerRegion::CENTER);
-            right = containerRegion->getUVs(Theme::ContainerRegion::RIGHT);
-            bottomLeft = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM_LEFT);
-            bottom = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM);
-            bottomRight = containerRegion->getUVs(Theme::ContainerRegion::BOTTOM_RIGHT);
-
-            // Calculate screen-space positions.
-            Theme::Border border = containerRegion->getBorder();
-            Theme::Padding padding = _style->getPadding();
-            Vector4 borderColor = containerRegion->getColor();
-
-            float midWidth = _size.x - border.left - border.right;
-            float midHeight = _size.y - border.top - border.bottom;
-            float midX = pos.x + border.left;
-            float midY = pos.y + border.top;
-            float rightX = pos.x + _size.x - border.right;
-            float bottomY = pos.y + _size.y - border.bottom;
-
-            // Draw themed border sprites.
-            if (!border.left && !border.right && !border.top && !border.bottom)
-            {
-                // No border, just draw the image.
-                spriteBatch->draw(pos.x, pos.y, _size.x, _size.y, center.u1, center.v1, center.u2, center.v2, borderColor, clip);
-            }
-            else
-            {
-                if (border.left && border.top)
-                    spriteBatch->draw(pos.x, pos.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, borderColor, clip);
-                if (border.top)
-                    spriteBatch->draw(pos.x + border.left, pos.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, borderColor, clip);
-                if (border.right && border.top)
-                    spriteBatch->draw(rightX, pos.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, borderColor, clip);
-                if (border.left)
-                    spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, borderColor, clip);
-                if (border.left && border.right && border.top && border.bottom)
-                    spriteBatch->draw(pos.x + border.left, pos.y + border.top, _size.x - border.left - border.right, _size.y - border.top - border.bottom,
-                        center.u1, center.v1, center.u2, center.v2, borderColor, clip);
-                if (border.right)
-                    spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, borderColor, clip);
-                if (border.bottom && border.left)
-                    spriteBatch->draw(pos.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, borderColor, clip);
-                if (border.bottom)
-                    spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, borderColor, clip);
-                if (border.bottom && border.right)
-                    spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, borderColor, clip);
-            }
+        //Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
+        const Theme::UVs& topLeft = getSkinUVs(Theme::Skin::TOP_LEFT, _state);
+        const Theme::UVs& top = getSkinUVs(Theme::Skin::TOP, _state);
+        const Theme::UVs& topRight = getSkinUVs(Theme::Skin::TOP_RIGHT, _state);
+        const Theme::UVs& left = getSkinUVs(Theme::Skin::LEFT, _state);
+        const Theme::UVs& center = getSkinUVs(Theme::Skin::CENTER, _state);
+        const Theme::UVs& right = getSkinUVs(Theme::Skin::RIGHT, _state);
+        const Theme::UVs& bottomLeft = getSkinUVs(Theme::Skin::BOTTOM_LEFT, _state);
+        const Theme::UVs& bottom = getSkinUVs(Theme::Skin::BOTTOM, _state);
+        const Theme::UVs& bottomRight = getSkinUVs(Theme::Skin::BOTTOM_RIGHT, _state);
+
+        // Calculate screen-space positions.
+        const Theme::Border& border = getBorder(_state);
+        const Theme::Padding& padding = getPadding();
+        Vector4 skinColor = getSkinColor(_state);
+        skinColor.w *= getOpacity(_state);
+
+        float midWidth = _size.x - border.left - border.right;
+        float midHeight = _size.y - border.top - border.bottom;
+        float midX = pos.x + border.left;
+        float midY = pos.y + border.top;
+        float rightX = pos.x + _size.x - border.right;
+        float bottomY = pos.y + _size.y - border.bottom;
+
+        // Draw themed border sprites.
+        if (!border.left && !border.right && !border.top && !border.bottom)
+        {
+            // No border, just draw the image.
+            spriteBatch->draw(pos.x, pos.y, _size.x, _size.y, center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+        }
+        else
+        {
+            if (border.left && border.top)
+                spriteBatch->draw(pos.x, pos.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, skinColor, clip);
+            if (border.top)
+                spriteBatch->draw(pos.x + border.left, pos.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, skinColor, clip);
+            if (border.right && border.top)
+                spriteBatch->draw(rightX, pos.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
+            if (border.left)
+                spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
+            if (border.left && border.right && border.top && border.bottom)
+                spriteBatch->draw(pos.x + border.left, pos.y + border.top, _size.x - border.left - border.right, _size.y - border.top - border.bottom,
+                    center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+            if (border.right)
+                spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
+            if (border.bottom && border.left)
+                spriteBatch->draw(pos.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, skinColor, clip);
+            if (border.bottom)
+                spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, skinColor, clip);
+            if (border.bottom && border.right)
+                spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, skinColor, clip);
         }
     }
 
-    void Control::drawSprites(SpriteBatch* spriteBatch, const Rectangle& position)
+    void Control::drawImages(SpriteBatch* spriteBatch, const Rectangle& position)
     {
     }
 
@@ -420,4 +754,207 @@ namespace gameplay
 
         return NORMAL;
     }
+
+    // Implementation of AnimationHandler
+    unsigned int Control::getAnimationPropertyComponentCount(int propertyId) const
+    {
+        switch(propertyId)
+        {
+        case ANIMATE_POSITION:
+        case ANIMATE_SIZE:
+            return 2;
+
+        case ANIMATE_POSITION_X:
+        case ANIMATE_POSITION_Y:
+        case ANIMATE_SIZE_WIDTH:
+        case ANIMATE_SIZE_HEIGHT:
+        case ANIMATE_OPACITY:
+            return 1;
+
+        default:
+            return -1;
+        }
+    }
+
+    void Control::getAnimationPropertyValue(int propertyId, AnimationValue* value)
+    {
+        switch(propertyId)
+        {
+        case ANIMATE_POSITION:
+            value->setFloat(0, _position.x);
+            value->setFloat(1, _position.y);
+            break;
+        case ANIMATE_SIZE:
+            value->setFloat(0, _size.x);
+            value->setFloat(1, _size.y);
+            break;
+        case ANIMATE_POSITION_X:
+            value->setFloat(0, _position.x);
+            break;
+        case ANIMATE_POSITION_Y:
+            value->setFloat(0, _position.y);
+            break;
+        case ANIMATE_SIZE_WIDTH:
+            value->setFloat(0, _size.x);
+            break;
+        case ANIMATE_SIZE_HEIGHT:
+            value->setFloat(0, _size.y);
+            break;
+        case ANIMATE_OPACITY:
+        default:
+            break;
+        }
+    }
+
+    void Control::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+    {
+        switch(propertyId)
+        {
+        case ANIMATE_POSITION:
+            applyAnimationValuePositionX(value->getFloat(0), blendWeight);
+            applyAnimationValuePositionY(value->getFloat(1), blendWeight);
+            break;
+        case ANIMATE_POSITION_X:
+            applyAnimationValuePositionX(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_POSITION_Y:
+            applyAnimationValuePositionY(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_SIZE:
+            applyAnimationValueSizeWidth(value->getFloat(0), blendWeight);
+            applyAnimationValueSizeHeight(value->getFloat(1), blendWeight);
+            break;
+        case ANIMATE_SIZE_WIDTH:
+            applyAnimationValueSizeWidth(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_SIZE_HEIGHT:
+            applyAnimationValueSizeHeight(value->getFloat(0), blendWeight);
+            break;
+        case ANIMATE_OPACITY:
+            applyAnimationValueOpacity();
+        default:
+            break;
+        }
+    }
+
+    void Control::applyAnimationValuePositionX(float x, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_POSITION_X_BIT) != ANIMATION_POSITION_X_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_POSITION_X_BIT;
+        }
+        else
+        {
+            x = Curve::lerp(blendWeight, _position.x, x);
+        }
+        _position.x = x;
+        _dirty = true;
+    }
+    
+    void Control::applyAnimationValuePositionY(float y, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_POSITION_Y_BIT) != ANIMATION_POSITION_Y_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_POSITION_Y_BIT;
+        }
+        else
+        {
+            y = Curve::lerp(blendWeight, _position.y, y);
+        }
+        _position.y = y;
+        _dirty = true;
+    }
+    
+    void Control::applyAnimationValueSizeWidth(float width, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_SIZE_WIDTH_BIT) != ANIMATION_SIZE_WIDTH_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_SIZE_WIDTH_BIT;
+        }
+        else
+        {
+            width = Curve::lerp(blendWeight, _size.x, width);
+        }
+        _size.x = width;
+        _dirty = true;
+    }
+
+    void Control::applyAnimationValueSizeHeight(float height, float blendWeight)
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_SIZE_HEIGHT_BIT) != ANIMATION_SIZE_HEIGHT_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_SIZE_HEIGHT_BIT;
+        }
+        else
+        {
+            height = Curve::lerp(blendWeight, _size.y, height);
+        }
+        _size.y = height;
+        _dirty = true;
+    }
+
+    void Control::applyAnimationValueOpacity()
+    {
+        if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
+        {
+            _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
+        }
+        _dirty = true;
+    }
+    
+    Theme::Style::Overlay** Control::getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays)
+    {
+        unsigned int index = 0;
+        if ((overlayTypes & NORMAL) == NORMAL)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
+        }
+
+        if ((overlayTypes & FOCUS) == FOCUS)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
+        }
+
+        if ((overlayTypes & ACTIVE) == ACTIVE)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+        }
+
+        if ((overlayTypes & DISABLED) == DISABLED)
+        {
+            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
+        }
+
+        return overlays;
+    }
+
+    Theme::Style::Overlay* Control::getOverlay(State state) const
+    {
+        switch(state)
+        {
+        case Control::NORMAL:
+            return _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
+        case Control::FOCUS:
+            return _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
+        case Control::ACTIVE:
+            return _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+        case Control::DISABLED:
+            return _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
+        default:
+            return NULL;
+        }
+    }
+
+    void Control::overrideStyle()
+    {
+        if (_styleOverridden)
+        {
+            return;
+        }
+
+        // Copy the style.
+        WARN_VARG("%d", sizeof(Theme::Style::Overlay));
+        _style = new Theme::Style(*_style);
+        _styleOverridden = true;
+    }
 }

+ 408 - 14
gameplay/src/Control.h

@@ -9,13 +9,18 @@
 #include "Touch.h"
 #include "Keyboard.h"
 
+/**
+ * Default duration of UI animations.
+ */
+#define DEFAULT_UI_ANIMATION_DURATION 200L
+
 namespace gameplay
 {
 
 /**
  * Base class for UI controls.
  */
-class Control : public Ref
+class Control : public Ref, public AnimationTarget
 {
     friend class Form;
     friend class Container;
@@ -29,12 +34,15 @@ public:
      */
     enum State
     {
-        NORMAL,
-        FOCUS,
-        ACTIVE,
-        DISABLED
+        NORMAL = 0x01,
+        FOCUS = 0x02,
+        ACTIVE = 0x04,
+        DISABLED = 0x08,
     };
 
+    // Only for setting control states
+    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
+
     class Listener
     {
     public:
@@ -50,6 +58,41 @@ public:
         virtual void controlEvent(Control* control, EventType evt) = 0;
     };
 
+    /**
+     * Position animation property. Data = x, y
+     */
+    static const int ANIMATE_POSITION = 1;
+
+    /**
+     * Position x animation property. Data = x
+     */
+    static const int ANIMATE_POSITION_X = 2;
+
+    /**
+     * Position y animation property. Data = y
+     */
+    static const int ANIMATE_POSITION_Y = 3;
+
+    /**
+     * Size animation property.  Data = width, height
+     */
+    static const int ANIMATE_SIZE = 4;
+
+    /**
+     * Size width animation property.  Data = width
+     */
+    static const int ANIMATE_SIZE_WIDTH = 5;
+
+    /**
+     * Size height animation property.  Data = height
+     */
+    static const int ANIMATE_SIZE_HEIGHT = 6;
+
+    /**
+     * Opacity property.  Data = opacity
+     */
+    static const int ANIMATE_OPACITY = 7;
+
     /**
      * Get this control's ID string.
      *
@@ -63,7 +106,7 @@ public:
      * @param x The x coordinate.
      * @param y The y coordinate.
      */
-    void setPosition(float x, float y);
+    void setPosition(float x, float y, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
 
     /**
      * Get the position of this control relative to its parent container.
@@ -78,7 +121,7 @@ public:
      * @param width The width.
      * @param height The height.
      */
-    void setSize(float width, float height);
+    void setSize(float width, float height, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
 
     /**
      * Get the desired size of this control, including its border and padding, before clipping.
@@ -87,6 +130,314 @@ public:
      */
     const Vector2& getSize() const;
 
+    // Themed properties.
+    
+    //void setBorder(const Theme::Border& border, unsigned char states = STATE_ALL);
+    /**
+     * Set the size of this control's border.
+     *
+     * @param top The height of the border's top side.
+     * @param bottom The height of the border's bottom side.
+     * @param left The width of the border's left side.
+     * @param right The width of the border's right side.
+     */
+    void setBorder(float top, float bottom, float left, float right, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the measurements of this control's border for a given state. 
+     *
+     * @return This control's border.
+     */
+    const Theme::Border& getBorder(State state = NORMAL) const;
+
+    /**
+     * Set the texture region of this control's skin.
+     *
+     * @param region The texture region, in pixels.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setSkinRegion(const Rectangle& region, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the texture region of this control's skin for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The texture region of this control's skin.
+     */
+    const Rectangle& getSkinRegion(State state = NORMAL) const;
+
+    /**
+     * Get the texture coordinates of an area of this control's skin for a given state.
+     *
+     * @param area The area of the skin to get the coordinates of.
+     * @param state The state to get this property from.
+     *
+     * @return The texture coordinates of an area of this control's skin.
+     */
+    const Theme::UVs& getSkinUVs(Theme::Skin::SkinArea area, State state = NORMAL) const;
+
+    /**
+     * Set the blend color of this control's skin.
+     *
+     * @param color The new blend color.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setSkinColor(const Vector4& color, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the blend color of this control's skin for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The blend color of this control's skin.
+     */
+    const Vector4& getSkinColor(State state = NORMAL) const;
+
+    /**
+     * Set this control's margin.
+     *
+     * @param top Height of top margin.
+     * @param bottom Height of bottom margin.
+     * @param left Width of left margin.
+     * @param right Width of right margin.
+     */
+    void setMargin(float top, float bottom, float left, float right);
+
+    /**
+     * Get this control's margin.
+     *
+     * @return This control's margin.
+     */
+    const Theme::Margin& getMargin() const;
+
+    /**
+     * Set this control's padding.
+     *
+     * @param top Height of top padding.
+     * @param bottom Height of bottom padding.
+     * @param left Width of left padding.
+     * @param right Width of right padding.
+     */
+    void setPadding(float top, float bottom, float left, float right);
+
+    /**
+     * Get this control's padding.
+     *
+     * @return This control's padding.
+     */
+    const Theme::Padding& getPadding() const;
+
+    /**
+     * Set the texture region of an image used by this control.
+     *
+     * @param id The ID of the image to modify.
+     * @param region The new texture region of the image.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setImageRegion(const char* id, const Rectangle& region, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the texture region of an image used by this control for a given state.
+     *
+     * @param id The ID of the image.
+     * @param state The state to get this property from.
+     *
+     * @return The texture region of the specified image.
+     */
+    const Rectangle& getImageRegion(const char* id, State state) const;
+
+    /**
+     * Set the blend color of an image used by this control.
+     *
+     * @param id The ID of the image to modify.
+     * @param color The new blend color of the image.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setImageColor(const char* id, const Vector4& color, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the blend color of an image used by this control for a given state.
+     *
+     * @param id The ID of the image.
+     * @param state The state to get this property from.
+     *
+     * @return The blend color of the specified image.
+     */
+    const Vector4& getImageColor(const char* id, State state) const;
+
+    /**
+     * Get the texture coordinates of an image used by this control for a given state.
+     *
+     * @param id The ID of the image.
+     * @param state The state to get this property from.
+     *
+     * @return The texture coordinates of the specified image.
+     */
+    const Theme::UVs& getImageUVs(const char* id, State state) const;
+
+    /**
+     * Set the texture region of this control's cursor.
+     *
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setCursorRegion(const Rectangle& region, unsigned char states);
+
+    /**
+     * Get the texture region of this control's cursor for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The texture region of this control's cursor.
+     */
+    const Rectangle& getCursorRegion(State state) const;
+
+    /**
+     * Set the blend color of this control's cursor.
+     *
+     * @param color The new blend color.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setCursorColor(const Vector4& color, unsigned char states);
+
+    /**
+     * Get the blend color of this control's cursor for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The blend color of this control's cursor.
+     */
+    const Vector4& getCursorColor(State state);
+    
+    /**
+     * Get the texture coordinates of this control's cursor for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The texture coordinates of this control's cursor.
+     */
+    const Theme::UVs& getCursorUVs(State state);
+
+    /**
+     * Set the font used by this control.
+     *
+     * @param font The new font to use.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setFont(Font* font, unsigned char states = STATE_ALL);
+
+    /**
+     * Get the font used by this control for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return the font used by this control.
+     */
+    Font* getFont(State state = NORMAL) const;
+
+    /**
+     * Set this control's font size.
+     *
+     * @param size The new font size.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setFontSize(unsigned int size, unsigned char states = STATE_ALL);
+
+    /**
+     * Get this control's font size for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return This control's font size.
+     */
+    unsigned int getFontSize(State state = NORMAL) const;
+
+    /**
+     * Set this control's text color.
+     *
+     * @param color The new text color.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setTextColor(const Vector4& color, unsigned char states = STATE_ALL);
+
+    /**
+     * Get this control's text color for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return This control's text color.
+     */
+    const Vector4& getTextColor(State state = NORMAL) const;
+
+    /**
+     * Set this control's text alignment.
+     *
+     * @param alignment The new text alignment.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setTextAlignment(Font::Justify alignment, unsigned char states = STATE_ALL);
+
+    /**
+     * Get this control's text alignment for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return This control's text alignment for the given state.
+     */
+    Font::Justify getTextAlignment(State state = NORMAL) const;
+
+    /**
+     * Set whether text is drawn from right to left within this control.
+     *
+     * @param rightToLeft Whether text is drawn from right to left within this control.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     */
+    void setTextRightToLeft(bool rightToLeft, unsigned char states = STATE_ALL);
+
+    /**
+     * Get whether text is drawn from right to left within this control, for a given state.
+     *
+     * @param state The state to get this property from.
+     *
+     * @return Whether text is drawn from right to left within this control, for the given state.
+     */
+    bool getTextRightToLeft(State state = NORMAL) const;
+
+    /**
+     * Set the opacity of this control.
+     *
+     * @param opacity The new opacity.
+     * @param states The states to set this property on.
+     *               One or more members of the Control::State enum, ORed together.
+     * @param duration The duration to animate opacity by.
+     */
+    void setOpacity(float opacity, unsigned char states = STATE_ALL, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
+
+    /**
+     * Get the opacity of this control for a given state. 
+     *
+     * @param state The state to get this property from.
+     *
+     * @return The opacity of this control for a given state.
+     */
+    float getOpacity(State state = NORMAL) const;
+
+    // TODO
+    // Controls must state the names of the images they use, for the purposes of a future UI editor.
+    //virtual std::vector<std::string> getImageNames() = 0;
+
+    // Control state.
     /**
      * Get the bounds of this control, relative to its parent container, after clipping.
      *
@@ -106,7 +457,7 @@ public:
      * its text and themed visual elements (CheckBox / RadioButton toggle etc.).
      *
      * Similarly set this on the width and/or height of a Container to tightly fit
-     * the Container around all its children.
+     * the Container around STATE_ALL its children.
      *
      * @param width Whether to automatically determine this Control's width.
      * @param height Whether to automatically determine this Control's height.
@@ -125,7 +476,7 @@ public:
      *
      * @return This control's current state.
      */
-    State getState();
+    State getState() const;
 
     /**
      * Disable this control.
@@ -185,6 +536,21 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+    /**
+     * @see AnimationTarget#getAnimationPropertyComponentCount
+     */
+    unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+
+    /**
+     * @see AnimationTarget#getAnimationProperty
+     */
+    void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+
+    /**
+     * @see AnimationTarget#setAnimationProperty
+     */
+    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
+
 protected:
     Control();
     virtual ~Control();
@@ -230,6 +596,7 @@ protected:
      */
     virtual void update(const Rectangle& clip);
 
+private:
     /**
      * Draws the themed border and background of a control.
      *
@@ -238,13 +605,14 @@ protected:
      */
     virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
 
+protected:
     /**
-     * Draw the icons associated with this control.
+     * Draw the images associated with this control.
      *
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param clip The clipping rectangle of this control's parent container.
      */
-    virtual void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     /**
      * Draw this control's text.
@@ -254,7 +622,7 @@ protected:
     virtual void drawText(const Rectangle& clip);
 
     /**
-     * Initialize properties common to all Controls.
+     * Initialize properties common to STATE_ALL Controls.
      */
     virtual void init(Theme::Style* style, Properties* properties);
 
@@ -274,7 +642,7 @@ protected:
     static State getStateFromString(const char* state);
 
     /**
-     * Notify all listeners of a specific event.
+     * Notify STATE_ALL listeners of a specific event.
      */
     void notifyListeners(Listener::EventType eventType);
 
@@ -285,7 +653,7 @@ protected:
     Vector2 _position;      // Position, relative to parent container's clipping window.
     Vector2 _size;          // Desired size.  Will be clipped.
     Rectangle _bounds;      // The position and size of this control, relative to parent container's bounds, including border and padding, after clipping.
-    Rectangle _textBounds;  // The position and size of this control's content, before clipping.  Used for text alignment.
+    Rectangle _textBounds;  // The position and size of this control's text area, before clipping.  Used for text alignment.
     Rectangle _clip;        // Clipping window of this control's content, after clipping.
     bool _autoWidth;
     bool _autoHeight;
@@ -296,6 +664,32 @@ protected:
     ListenerMap* _listeners;
 
 private:
+    // Animation blending bits.
+    static const char ANIMATION_POSITION_X_BIT = 0x01;
+    static const char ANIMATION_POSITION_Y_BIT = 0x02;
+    static const char ANIMATION_SIZE_WIDTH_BIT = 0x04;
+    static const char ANIMATION_SIZE_HEIGHT_BIT = 0x08;
+    static const char ANIMATION_OPACITY_BIT = 0x10;
+
+    bool _styleOverridden;
+
+    void applyAnimationValuePositionX(float x, float blendWeight);
+    void applyAnimationValuePositionY(float y, float blendWeight);
+    void applyAnimationValueSizeWidth(float width, float blendWeight);
+    void applyAnimationValueSizeHeight(float height, float blendWeight);
+    void applyAnimationValueOpacity();
+
+    // Gets the overlays requested in the overlayTypes bitflag.
+    Theme::Style::Overlay** getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays);
+
+    /**
+     * Gets an overlay from a control state.
+     */
+    Theme::Style::Overlay* getOverlay(Control::State state) const;
+
+    // Ensures that this control has a copy of its style so that it can override it without affecting other controls.
+    void overrideStyle();
+
     Control(const Control& copy);
 };
 

+ 148 - 1
gameplay/src/DebugNew.cpp

@@ -5,6 +5,15 @@
 #include <cstdio>
 #include <cstdarg>
 
+#ifdef WIN32
+#include <windows.h>
+#include <dbghelp.h>
+#pragma comment(lib,"dbghelp.lib")
+
+#define MAX_STACK_FRAMES 16
+bool __trackStackTrace = false;
+#endif
+
 struct MemoryAllocationRecord
 {
     unsigned long address;          // address returned to the caller after allocation
@@ -13,6 +22,10 @@ struct MemoryAllocationRecord
     int line;                       // source line of the allocation request
     MemoryAllocationRecord* next;
     MemoryAllocationRecord* prev;
+#ifdef WIN32
+    bool trackStackTrace;
+    unsigned int pc[MAX_STACK_FRAMES];
+#endif
 };
 
 MemoryAllocationRecord* __memoryAllocations = 0;
@@ -99,6 +112,54 @@ void* debugAlloc(std::size_t size, const char* file, int line)
     rec->next = __memoryAllocations;
     rec->prev = 0;
 
+    // Capture the stack frame (up to MAX_STACK_FRAMES) if we 
+    // are running on Windows and the user has enabled it.
+#if defined(WIN32)
+    rec->trackStackTrace = __trackStackTrace;
+    if (rec->trackStackTrace)
+    {
+        static bool initialized = false;
+        if (!initialized)
+        {
+            if (!SymInitialize(GetCurrentProcess(), NULL, true))
+                gameplay::printError("Stack trace tracking will not work.");
+            initialized = true;
+        }
+    
+        // Get the current context (state of EBP, EIP, ESP registers).
+        static CONTEXT context;
+        RtlCaptureContext(&context);
+    
+        static STACKFRAME64 stackFrame;
+        memset(&stackFrame, 0, sizeof(STACKFRAME64));
+
+        // Initialize the stack frame based on the machine architecture.
+#ifdef _M_IX86
+        static const DWORD machineType = IMAGE_FILE_MACHINE_I386;
+        stackFrame.AddrPC.Offset = context.Eip;
+        stackFrame.AddrPC.Mode = AddrModeFlat;
+        stackFrame.AddrFrame.Offset = context.Ebp;
+        stackFrame.AddrFrame.Mode = AddrModeFlat;
+        stackFrame.AddrStack.Offset = context.Esp;
+        stackFrame.AddrStack.Mode = AddrModeFlat;
+#else
+#error "Machine architecture not supported!"
+#endif
+
+        // Walk up the stack and store the program counters.
+        memset(rec->pc, 0, sizeof(rec->pc));
+        for (int i = 0; i < MAX_STACK_FRAMES; i++)
+        {
+            rec->pc[i] = stackFrame.AddrPC.Offset;
+            if (!StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &stackFrame,
+                &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
+            {
+                break;
+            }
+        }
+    }
+#endif
+
     if (__memoryAllocations)
         __memoryAllocations->prev = rec;
     __memoryAllocations = rec;
@@ -137,6 +198,70 @@ void debugFree(void* p)
     free(mem);
 }
 
+#ifdef WIN32
+void printStackTrace(MemoryAllocationRecord* rec)
+{
+    const unsigned int bufferSize = 512;
+
+    // Resolve the program counter to the corresponding function names.
+    unsigned int pc;
+    for (int i = 0; i < MAX_STACK_FRAMES; i++)
+    {
+        // Check to see if we are at the end of the stack trace.
+        pc = rec->pc[i];
+        if (pc == 0)
+            break;
+
+        // Get the function name.
+        unsigned char buffer[sizeof(IMAGEHLP_SYMBOL64) + bufferSize];
+        IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)buffer;
+        DWORD64 displacement;
+        memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + bufferSize);
+        symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+        symbol->MaxNameLength = bufferSize;
+        if (!SymGetSymFromAddr64(GetCurrentProcess(), pc, &displacement, symbol))
+        {
+            gameplay::printError("[memory] STACK TRACE: <unknown location>");
+        }
+        else
+        {
+            symbol->Name[bufferSize - 1] = '\0';
+
+            // Check if we need to go further up the stack.
+            if (strncmp(symbol->Name, "operator new", 12) == 0)
+            {
+                // In operator new or new[], keep going...
+            }
+            else
+            {
+                // Get the file and line number.
+                if (pc != 0)
+                {
+                    IMAGEHLP_LINE64 line;
+                    DWORD displacement;
+                    memset(&line, 0, sizeof(line));
+                    line.SizeOfStruct = sizeof(line);
+                    if (!SymGetLineFromAddr64(GetCurrentProcess(), pc, &displacement, &line))
+                    {
+                        gameplay::printError("[memory] STACK TRACE: %s - <unknown file>:<unknown line number>", symbol->Name);
+                    }
+                    else
+                    {
+                        const char* file = strrchr(line.FileName, '\\');
+                        if(!file) 
+                            file = line.FileName;
+                        else
+                            file++;
+                        
+                        gameplay::printError("[memory] STACK TRACE: %s - %s:%d", symbol->Name, file, line.LineNumber);
+                    }
+                }
+            }
+        }
+    }
+}
+#endif
+
 extern void printMemoryLeaks()
 {
     // Dump general heap memory leaks
@@ -150,10 +275,32 @@ extern void printMemoryLeaks()
         MemoryAllocationRecord* rec = __memoryAllocations;
         while (rec)
         {
-            gameplay::printError("[memory] LEAK: HEAP allocation leak of size %d leak from line %d in file '%s'.", rec->size, rec->line, rec->file);
+#ifdef WIN32
+            if (rec->trackStackTrace)
+            {
+                gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d:", rec->address, rec->size);
+                printStackTrace(rec);
+            }
+            else
+                gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.", rec->address, rec->size, rec->line, rec->file);
+#else
+            gameplay::printError("[memory] LEAK: HEAP allocation leak at address %#x of size %d from line %d in file '%s'.", rec->address, rec->size, rec->line, rec->file);
+#endif
             rec = rec->next;
         }
     }
 }
 
+#if defined(WIN32)
+void setTrackStackTrace(bool trackStackTrace)
+{
+    __trackStackTrace = trackStackTrace;
+}
+
+void toggleTrackStackTrace()
+{
+    __trackStackTrace = !__trackStackTrace;
+}
+#endif
+
 #endif

+ 14 - 0
gameplay/src/DebugNew.h

@@ -89,4 +89,18 @@ T* bullet_new(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9)
 #endif
 }
 
+#if defined(WIN32)
+/**
+ * Sets whether stack traces are tracked on memory allocations or not.
+ * 
+ * @param trackStackTrace Whether to track the stack trace on memory allocations.
+ */
+void setTrackStackTrace(bool trackStackTrace);
+
+/**
+ * Toggles stack trace tracking on memory allocations.
+ */
+void toggleTrackStackTrace();
+#endif
+
 #endif

+ 7 - 4
gameplay/src/Font.h

@@ -149,8 +149,8 @@ public:
      * @param rightToLeft Whether to draw text from right to left.
      * @param clip A region to clip text within after applying justification to the viewport area.
      */
-    void drawText(const char* text, const Rectangle& area, const Vector4& color,
-        unsigned int size = 0, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
+    void drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0, 
+				  Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
 
     /**
      * Measures a string's width and height without alignment, wrapping or clipping.
@@ -217,12 +217,14 @@ private:
 
     // Utilities
     unsigned int getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale);
+
     unsigned int getReversedTokenLength(const char* token, const char* bufStart);
 
     // Returns 0 if EOF was reached, 1 if delimiters were handles correctly, and 2 if the stopAtPosition was reached while handling delimiters.
     int handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
-                          std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd, unsigned int* charIndex = NULL,
-                          const Vector2* stopAtPosition = NULL, const int currentIndex = -1, const int destIndex = -1);
+                         std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd, unsigned int* charIndex = NULL,
+                         const Vector2* stopAtPosition = NULL, const int currentIndex = -1, const int destIndex = -1);
+
     void addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Justify hAlign,
                      std::vector<int>* xPositions, std::vector<unsigned int>* lineLengths, bool rightToLeft);
 
@@ -235,6 +237,7 @@ private:
     unsigned int _glyphCount;
     Texture* _texture;
     SpriteBatch* _batch;
+	Rectangle _viewport;
 };
 
 }

+ 14 - 14
gameplay/src/Form.cpp

@@ -13,7 +13,7 @@ namespace gameplay
 {
     static std::vector<Form*> __forms;
 
-    Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL), _viewport(NULL)
+    Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL)
     {
     }
 
@@ -27,7 +27,6 @@ namespace gameplay
         SAFE_RELEASE(_node);
         SAFE_RELEASE(_frameBuffer);
         SAFE_RELEASE(_theme);
-        SAFE_DELETE(_viewport);
 
         // Remove this Form from the global list.
         std::vector<Form*>::iterator it = std::find(__forms.begin(), __forms.end(), this);
@@ -45,9 +44,7 @@ namespace gameplay
         Properties* properties = Properties::create(path);
         assert(properties);
         if (properties == NULL)
-        {
             return NULL;
-        }
 
         // Check if the Properties is valid and has a valid namespace.
         Properties* formProperties = properties->getNextNamespace();
@@ -121,14 +118,14 @@ namespace gameplay
     void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
     {
         Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);
-        initQuad(mesh);
+        initializeQuad(mesh);
         SAFE_RELEASE(mesh);
     }
 
     void Form::setQuad(float x, float y, float width, float height)
     {
         Mesh* mesh = Mesh::createQuad(x, y, width, height);
-        initQuad(mesh);
+        initializeQuad(mesh);
         SAFE_RELEASE(mesh);
     }
 
@@ -143,7 +140,6 @@ namespace gameplay
 
             Matrix::createOrthographicOffCenter(0, _size.x, _size.y, 0, 0, 1, &_projectionMatrix);
             _theme->setProjectionMatrix(_projectionMatrix);
-            _viewport = new Viewport(0, 0, _size.x, _size.y);
             
             _node->setModel(_quad);
         }
@@ -171,14 +167,19 @@ namespace gameplay
             if (isDirty())
             {
                 _frameBuffer->bind();
-                _viewport->bind();
+
+				Game* game = Game::getInstance();
+				Rectangle prevViewport = game->getViewport();
+                
+				game->setViewport(Rectangle(_position.x, _position.y, _size.x, _size.y));
 
                 draw(_theme->getSpriteBatch(), _clip);
 
                 // Rebind the default framebuffer and game viewport.
                 FrameBuffer::bindDefault();
-                Game* game = Game::getInstance();
-                GL_ASSERT( glViewport(0, 0, game->getWidth(), game->getHeight()) );
+
+				// restore the previous game viewport
+				game->setViewport(prevViewport);
             }
 
             _quad->draw();
@@ -210,7 +211,7 @@ namespace gameplay
                 control->drawBorder(spriteBatch, clip);
 
                 // Add all themed foreground sprites (checkboxes etc.) to the same batch.
-                control->drawSprites(spriteBatch, clip);
+                control->drawImages(spriteBatch, clip);
             }
         }
         spriteBatch->end();
@@ -229,7 +230,7 @@ namespace gameplay
         _dirty = false;
     }
 
-    void Form::initQuad(Mesh* mesh)
+    void Form::initializeQuad(Mesh* mesh)
     {
         // Release current model.
         SAFE_RELEASE(_quad);
@@ -266,7 +267,6 @@ namespace gameplay
         Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
         sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
         material->getParameter("u_texture")->setValue(sampler);
-
         material->getParameter("u_textureRepeat")->setValue(Vector2::one());
         material->getParameter("u_textureTransform")->setValue(Vector2::zero());
 
@@ -299,7 +299,7 @@ namespace gameplay
 
                         // Unproject point into world space.
                         Ray ray;
-                        camera->pickRay(NULL, x, y, &ray);
+						camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
 
                         // Find the quad's plane.
                         // We know its normal is the quad's forward vector.

+ 6 - 3
gameplay/src/Form.h

@@ -22,6 +22,7 @@ class Form : public Container
     friend class Platform;
 
 public:
+
     /**
      * Create from properties file.
      * The top-most namespace in the file must be named 'form'.  The following properties are available for forms:
@@ -105,7 +106,9 @@ public:
     void draw();
 
 protected:
-    Form();
+    
+	Form();
+
     virtual ~Form();
 
     static Form* create(const char* textureFile, Layout::Type type);
@@ -115,7 +118,7 @@ protected:
      *
      * @param mesh The mesh to create a model from.
      */
-    void initQuad(Mesh* mesh);
+    void initializeQuad(Mesh* mesh);
 
     /**
      * Draw this form into the current framebuffer.
@@ -142,9 +145,9 @@ protected:
     Node* _node;                // Node for transforming this Form in world-space.
     FrameBuffer* _frameBuffer;  // FBO the Form is rendered into for texturing the quad.
     Matrix _projectionMatrix;   // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
-    Viewport* _viewport;        // Viewport for setting before rendering into the FBO.
 
 private:
+
     Form(const Form& copy);
 };
 

+ 14 - 5
gameplay/src/Game.cpp

@@ -21,6 +21,7 @@ Game::Game()
 {
     assert(__gameInstance == NULL);
     __gameInstance = this;
+    _timeEvents = new std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> >();
 }
 
 Game::Game(const Game& copy)
@@ -31,7 +32,7 @@ Game::~Game()
 {
     // Do not call any virtual functions from the destructor.
     // Finalization is done from outside this class.
-
+    delete _timeEvents;
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION
     Ref::printLeaks();
     printMemoryLeaks();
@@ -92,6 +93,8 @@ bool Game::startup()
     if (_state != UNINITIALIZED)
         return false;
 
+	setViewport(Rectangle(0.0f, 0.0f, (float)_width, (float)_height));
+
     RenderState::initialize();
 
     _animationController = new AnimationController();
@@ -212,6 +215,12 @@ void Game::frame()
     }
 }
 
+void Game::setViewport(const Rectangle& viewport)
+{
+	_viewport = viewport;
+	glViewport((GLuint)viewport.x, (GLuint)viewport.y, (GLuint)viewport.width, (GLuint)viewport.height); 
+}
+
 void Game::clear(ClearFlags flags, const Vector4& clearColor, float clearDepth, int clearStencil)
 {
     GLbitfield bits = 0;
@@ -280,7 +289,7 @@ void Game::schedule(long timeOffset, TimeListener* timeListener, void* cookie)
 {
     assert(timeListener);
     TimeEvent timeEvent(getGameTime() + timeOffset, timeListener, cookie);
-    _timeEvents.push(timeEvent);
+    _timeEvents->push(timeEvent);
 }
 
 bool Game::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
@@ -304,15 +313,15 @@ void Game::updateOnce()
 
 void Game::fireTimeEvents(long frameTime)
 {
-    while (_timeEvents.size() > 0)
+    while (_timeEvents->size() > 0)
     {
-        const TimeEvent* timeEvent = &_timeEvents.top();
+        const TimeEvent* timeEvent = &_timeEvents->top();
         if (timeEvent->time > frameTime)
         {
             break;
         }
         timeEvent->listener->timeEvent(frameTime - timeEvent->time, timeEvent->cookie);
-        _timeEvents.pop();
+        _timeEvents->pop();
     }
 }
 

+ 32 - 1
gameplay/src/Game.h

@@ -10,6 +10,7 @@
 #include "AnimationController.h"
 #include "PhysicsController.h"
 #include "AudioListener.h"
+#include "Rectangle.h"
 #include "Vector4.h"
 #include "TimeListener.h"
 
@@ -151,6 +152,22 @@ public:
      */
     inline unsigned int getHeight() const;
 
+	/**
+	 * Gets the game current viewport.
+	 *
+	 * The default viewport is Rectangle(0, 0, Game::getWidth(), Game::getHeight()).
+	 */
+	inline const Rectangle& getViewport() const;
+
+	/**
+	 * Set the game current viewport.
+	 *
+	 * The x, y, width and height of the viewport must all be positive.
+	 *
+	 * viewport The custom viewport to be set on the game.
+	 */
+	void setViewport(const Rectangle& viewport);
+
     /**
      * Clears the specified resource buffers to the specified clear values. 
      *
@@ -385,6 +402,7 @@ private:
     unsigned int _frameRate;                    // The current frame rate.
     unsigned int _width;                        // The game's display width.
     unsigned int _height;                       // The game's display height.
+	Rectangle _viewport;						// the games's current viewport.
     Vector4 _clearColor;                        // The clear color value last used for clearing the color buffer.
     float _clearDepth;                          // The clear depth value last used for clearing the depth buffer.
     int _clearStencil;                          // The clear stencil value last used for clearing the stencil buffer.
@@ -392,7 +410,9 @@ private:
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     PhysicsController* _physicsController;      // Controls the simulation of a physics scene and entities.
     AudioListener* _audioListener;              // The audio listener in 3D space.
-    std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> > _timeEvents; // Contains the scheduled time events.
+    std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> >* _timeEvents; // Contains the scheduled time events.
+
+    // Note: Do not add STL object member variables on the stack; this will cause false memory leaks to be reported.
 
     friend class SplashDisplayer;
 };
@@ -425,6 +445,17 @@ private:
     long _startTime;
 };
 
+/**
+ * Displays a splash screen using the {@link Game#renderOnce} mechanism for at least the given amount
+ * of time. This function is intended to be called at the beginning of a block of code that is be 
+ * executed while the splash screen is displayed (i.e. Game#initialize). This function will block 
+ * at the end of the block of code in which it is called for the amount of time that has not yet elapsed.
+ * 
+ * @param instance See {@link Game#renderOnce}.
+ * @param method See {@link Game#renderOnce}.
+ * @param cookie See {@link Game#renderOnce}.
+ * @param time The minimum amount of time to display the splash screen (in milliseconds).
+ */
 #define displaySplash(instance, method, cookie, time) \
     SplashDisplayer __##instance##SplashDisplayer; \
     __##instance##SplashDisplayer.run(instance, method, cookie, time)

+ 5 - 0
gameplay/src/Game.inl

@@ -24,6 +24,11 @@ inline unsigned int Game::getHeight() const
     return _height;
 }
 
+inline const Rectangle& Game::getViewport() const
+{
+	return _viewport;
+}
+
 inline AnimationController* Game::getAnimationController() const
 {
     return _animationController;

+ 1 - 1
gameplay/src/Joint.cpp

@@ -19,7 +19,7 @@ Joint* Joint::create(const char* id)
     return new Joint(id);
 }
 
-Node* Joint::cloneSingleNode(CloneContext &context) const
+Node* Joint::cloneSingleNode(NodeCloneContext &context) const
 {
     Joint* copy = Joint::create(getId());
     context.registerClonedNode(this, copy);

+ 1 - 1
gameplay/src/Joint.h

@@ -61,7 +61,7 @@ protected:
      * 
      * @return Pointer to the newly created joint.
      */
-    virtual Node* cloneSingleNode(CloneContext &context) const;
+    virtual Node* cloneSingleNode(NodeCloneContext &context) const;
 
     /**
      * Sets the inverse bind pose matrix.

+ 4 - 1
gameplay/src/Label.cpp

@@ -73,9 +73,12 @@ namespace gameplay
         Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
         Font* font = overlay->getFont();
 
+        Vector4 textColor = overlay->getTextColor();
+        textColor.w *= overlay->getOpacity();
+
         // Draw the text.
         font->begin();
-        font->drawText(_text.c_str(), _textBounds, overlay->getTextColor(), overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft(), &_clip);
+        font->drawText(_text.c_str(), _textBounds, textColor, overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft(), &_clip);
         font->end();
 
         _dirty = false;

+ 1 - 1
gameplay/src/Light.cpp

@@ -196,7 +196,7 @@ float Light::getOuterAngleCos()  const
     return _spot->outerAngleCos;
 }
 
-Light* Light::clone(CloneContext &context) const
+Light* Light::clone(NodeCloneContext &context) const
 {
     Light* lightClone = NULL;
     switch (_type)

+ 2 - 2
gameplay/src/Light.h

@@ -3,12 +3,12 @@
 
 #include "Ref.h"
 #include "Vector3.h"
-#include "CloneContext.h"
 
 namespace gameplay
 {
 
 class Node;
+class NodeCloneContext;
 
 /**
  * Defines a light.
@@ -231,7 +231,7 @@ private:
      * 
      * @return The newly created light.
      */
-    Light* clone(CloneContext &context) const;
+    Light* clone(NodeCloneContext &context) const;
 
     Light::Type _type;
     union

+ 2 - 1
gameplay/src/Material.cpp

@@ -5,6 +5,7 @@
 #include "Technique.h"
 #include "Pass.h"
 #include "Properties.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -125,7 +126,7 @@ Material* Material::create(const char* vshPath, const char* fshPath, const char*
     return material;
 }
 
-Material* Material::clone(CloneContext &context) const
+Material* Material::clone(NodeCloneContext &context) const
 {
     Material* material = new Material();
     RenderState::cloneInto(material, context);

+ 3 - 1
gameplay/src/Material.h

@@ -8,6 +8,8 @@
 namespace gameplay
 {
 
+class NodeCloneContext;
+
 /**
  * Defines a material for an object to be rendered.
  *
@@ -75,7 +77,7 @@ public:
      * 
      * @return The newly created material.
      */
-    Material* clone(CloneContext &context) const;
+    Material* clone(NodeCloneContext &context) const;
 
     /**
      * Returns the number of techniques in the material.

+ 1 - 1
gameplay/src/Model.cpp

@@ -359,7 +359,7 @@ void Model::validatePartCount()
     }
 }
 
-Model* Model::clone(CloneContext &context)
+Model* Model::clone(NodeCloneContext &context)
 {
     Model* model = Model::create(getMesh());
     if (getSkin())

+ 2 - 1
gameplay/src/Model.h

@@ -11,6 +11,7 @@ namespace gameplay
 class Package;
 class MeshSkin;
 class Node;
+class NodeCloneContext;
 
 /**
  * Defines a Model which is an instance of a Mesh that can be drawn
@@ -184,7 +185,7 @@ private:
      * 
      * @return The new cloned model.
      */
-    Model* clone(CloneContext &context);
+    Model* clone(NodeCloneContext &context);
 
     Mesh* _mesh;
     Material* _material;

+ 68 - 9
gameplay/src/Node.cpp

@@ -722,11 +722,11 @@ const BoundingSphere& Node::getBoundingSphere() const
 
 Node* Node::clone() const
 {
-    CloneContext context;
+    NodeCloneContext context;
     return cloneRecursive(context);
 }
 
-Node* Node::cloneSingleNode(CloneContext &context) const
+Node* Node::cloneSingleNode(NodeCloneContext &context) const
 {
     Node* copy = Node::create(getId());
     context.registerClonedNode(this, copy);
@@ -734,7 +734,7 @@ Node* Node::cloneSingleNode(CloneContext &context) const
     return copy;
 }
 
-Node* Node::cloneRecursive(CloneContext &context) const
+Node* Node::cloneRecursive(NodeCloneContext &context) const
 {
     Node* copy = cloneSingleNode(context);
 
@@ -747,7 +747,7 @@ Node* Node::cloneRecursive(CloneContext &context) const
     return copy;
 }
 
-void Node::cloneInto(Node* node, CloneContext &context) const
+void Node::cloneInto(Node* node, NodeCloneContext &context) const
 {
     Transform::cloneInto(node, context);
 
@@ -868,21 +868,80 @@ PhysicsCollisionObject* Node::setCollisionObject(PhysicsCollisionObject::Type ty
 
 PhysicsCollisionObject* Node::setCollisionObject(const char* filePath)
 {
-    SAFE_DELETE(_collisionObject);
+    // Load the collision object properties from file.
+    Properties* properties = Properties::create(filePath);
+    assert(properties);
+    if (properties == NULL)
+    {
+        WARN_VARG("Failed to load collision object file: %s", filePath);
+        return NULL;
+    }
 
-	// TODO: Support other collision object types from file
-    _collisionObject = PhysicsRigidBody::create(this, filePath);
+    PhysicsCollisionObject* collisionObject = setCollisionObject(properties->getNextNamespace());
+    SAFE_DELETE(properties);
 
-	return _collisionObject;
+    return collisionObject;
 }
 
 PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
 {
     SAFE_DELETE(_collisionObject);
 
-    _collisionObject = PhysicsRigidBody::create(this, properties);
+    // Check if the properties is valid.
+    if (!properties || 
+        !(strcmp(properties->getNamespace(), "character") == 0 || 
+        strcmp(properties->getNamespace(), "ghost") == 0 || 
+        strcmp(properties->getNamespace(), "rigidbody") == 0))
+    {
+        WARN("Failed to load collision object from properties object: must be non-null object and have namespace equal to \'character\', \'ghost\', or \'rigidbody\'.");
+        return NULL;
+    }
 
+    if (strcmp(properties->getNamespace(), "character") == 0)
+    {
+        _collisionObject = PhysicsCharacter::create(this, properties);
+    }
+    else if (strcmp(properties->getNamespace(), "ghost") == 0)
+    {
+        _collisionObject = PhysicsGhostObject::create(this, properties);
+    }
+    else if (strcmp(properties->getNamespace(), "rigidbody") == 0)
+    {
+        _collisionObject = PhysicsRigidBody::create(this, properties);
+    }
 	return _collisionObject;
 }
 
+NodeCloneContext::NodeCloneContext()
+{
+    
+}
+
+NodeCloneContext::~NodeCloneContext()
+{
+
+}
+
+Animation* NodeCloneContext::findClonedAnimation(const Animation* animation)
+{
+    AnimationMap::iterator it = _clonedAnimations.find(animation);
+    return it != _clonedAnimations.end() ? it->second : NULL;
+}
+
+void NodeCloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
+{
+    _clonedAnimations[original] = clone;
+}
+
+Node* NodeCloneContext::findClonedNode(const Node* node)
+{
+    NodeMap::iterator it = _clonedNodes.find(node);
+    return it != _clonedNodes.end() ? it->second : NULL;
+}
+
+void NodeCloneContext::registerClonedNode(const Node* original, Node* clone)
+{
+    _clonedNodes[original] = clone;
+}
+
 }

+ 76 - 3
gameplay/src/Node.h

@@ -498,7 +498,7 @@ protected:
      * 
      * @return Pointer to the newly created node.
      */
-    virtual Node* cloneSingleNode(CloneContext &context) const;
+    virtual Node* cloneSingleNode(NodeCloneContext &context) const;
 
     /**
      * Recursively clones this node and its children.
@@ -507,7 +507,7 @@ protected:
      * 
      * @return The newly created node.
      */
-    Node* cloneRecursive(CloneContext &context) const;
+    Node* cloneRecursive(NodeCloneContext &context) const;
 
     /**
      * Copies the data from this node into the given node.
@@ -515,7 +515,7 @@ protected:
      * @param node The node to copy the data to.
      * @param context The clone context.
      */
-    void cloneInto(Node* node, CloneContext &context) const;
+    void cloneInto(Node* node, NodeCloneContext &context) const;
 
     /**
      * Removes this node from its parent.
@@ -572,6 +572,79 @@ protected:
     mutable BoundingSphere _bounds;
 };
 
+/**
+ * NodeCloneContext represents the context data that is kept when cloning a node.
+ * 
+ * The NodeCloneContext is used to make sure objects don't get cloned twice.
+ */
+class NodeCloneContext
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    NodeCloneContext();
+
+    /**
+     * Destructor.
+     */
+    ~NodeCloneContext();
+
+    /**
+     * Finds the cloned animation of the given animation or NULL if this animation was not registered with this context.
+     * 
+     * @param animation The animation to search for the cloned copy of.
+     * 
+     * @return The cloned animation or NULL if not found.
+     */
+    Animation* findClonedAnimation(const Animation* animation);
+
+    /**
+     * Registers the cloned animation with this context so that it doesn't get cloned twice.
+     * 
+     * @param original The pointer to the original animation.
+     * @param clone The pointer to the cloned animation.
+     */
+    void registerClonedAnimation(const Animation* original, Animation* clone);
+
+    /**
+     * Finds the cloned node of the given node or NULL if this node was not registered with this context.
+     * 
+     * @param node The node to search for the cloned copy of.
+     * 
+     * @return The cloned node or NULL if not found.
+     */
+    Node* findClonedNode(const Node* node);
+
+    /**
+     * Registers the cloned node with this context so that it doens't get cloned twice.
+     * 
+     * @param original The pointer to the original node.
+     * @param clone The pointer to the cloned node.
+     */
+    void registerClonedNode(const Node* original, Node* clone);
+
+private:
+    
+    /**
+     * Hidden copy constructor.
+     */
+    NodeCloneContext(const NodeCloneContext&);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    NodeCloneContext& operator=(const NodeCloneContext&);
+
+private:
+    typedef std::map<const Animation*, Animation*> AnimationMap;
+    typedef std::map<const Node*, Node*> NodeMap;
+
+    AnimationMap _clonedAnimations;
+    NodeMap _clonedNodes;
+};
+
 }
 
 #endif

+ 2 - 1
gameplay/src/Pass.cpp

@@ -2,6 +2,7 @@
 #include "Pass.h"
 #include "Technique.h"
 #include "Material.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -79,7 +80,7 @@ void Pass::unbind()
     }
 }
 
-Pass* Pass::clone(Technique* technique, CloneContext &context) const
+Pass* Pass::clone(Technique* technique, NodeCloneContext &context) const
 {
     Effect* effect = getEffect();
     effect->addRef();

+ 2 - 1
gameplay/src/Pass.h

@@ -8,6 +8,7 @@ namespace gameplay
 {
 
 class Technique;
+class NodeCloneContext;
 
 /**
  * Defines a pass for an object to be rendered.
@@ -100,7 +101,7 @@ private:
      * 
      * @return The newly created Pass.
      */
-    Pass* clone(Technique* technique, CloneContext &context) const;
+    Pass* clone(Technique* technique, NodeCloneContext &context) const;
 
     std::string _id;
     Technique* _technique;

+ 25 - 0
gameplay/src/PhysicsCharacter.cpp

@@ -76,6 +76,31 @@ PhysicsCharacter::~PhysicsCharacter()
     Game::getInstance()->getPhysicsController()->_world->removeAction(this);
 }
 
+PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
+{
+    // Check if the properties is valid and has a valid namespace.
+    assert(properties);
+    if (!properties || !(strcmp(properties->getNamespace(), "character") == 0))
+    {
+        WARN("Failed to load physics character from properties object: must be non-null object and have namespace equal to \'character\'.");
+        return NULL;
+    }
+
+    // Load the physics collision shape definition.
+    PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
+    if (shape == NULL)
+    {
+        WARN("Failed to create collision shape during physics character creation.");
+        return NULL;
+    }
+
+    // Create the physics character.
+    PhysicsCharacter* character = new PhysicsCharacter(node, *shape);
+    SAFE_DELETE(shape);
+
+    return character;
+}
+
 PhysicsCollisionObject::Type PhysicsCharacter::getType() const
 {
     return PhysicsCollisionObject::CHARACTER;

+ 12 - 0
gameplay/src/PhysicsCharacter.h

@@ -1,7 +1,9 @@
 #ifndef PHYSICSCHARACTER_H_
 #define PHYSICSCHARACTER_H_
 
+#include "Node.h"
 #include "PhysicsGhostObject.h"
+#include "Properties.h"
 
 namespace gameplay
 {
@@ -296,6 +298,16 @@ private:
      */
     virtual ~PhysicsCharacter();
 
+    /**
+     * Creates a physics character from the specified properties object.
+     * 
+     * @param node The node to create a physics character for; note that the node must have
+     *      a model attached to it prior to creating a physics character for it.
+     * @param properties The properties object defining the physics character (must have namespace equal to 'character').
+     * @return The newly created physics character, or <code>NULL</code> if the physics character failed to load.
+     */
+    static PhysicsCharacter* create(Node* node, Properties* properties);
+
     void updateCurrentVelocity();
 
     void play(CharacterAnimation* animation, unsigned int layer);

+ 408 - 160
gameplay/src/PhysicsCollisionShape.cpp

@@ -1,160 +1,408 @@
-#include "Base.h"
-#include "PhysicsCollisionShape.h"
-
-namespace gameplay
-{
-
-PhysicsCollisionShape::PhysicsCollisionShape(Type type, btCollisionShape* shape)
-	: _type(type), _shape(shape)
-{
-	memset(&_shapeData, 0, sizeof(_shapeData));
-}
-
-PhysicsCollisionShape::PhysicsCollisionShape(const PhysicsCollisionShape& copy)
-{
-	// hidden
-}
-
-PhysicsCollisionShape::~PhysicsCollisionShape()
-{
-	if (_shape)
-	{
-		// Cleanup shape-specific cached data
-		switch (_type)
-		{
-		case SHAPE_MESH:
-			if (_shapeData.meshData)
-			{
-				SAFE_DELETE_ARRAY(_shapeData.meshData->vertexData);
-				for (unsigned int i = 0; i < _shapeData.meshData->indexData.size(); i++)
-				{
-					SAFE_DELETE_ARRAY(_shapeData.meshData->indexData[i]);
-				}
-				SAFE_DELETE(_shapeData.meshData);
-			}
-			break;
-		case SHAPE_HEIGHTFIELD:
-			if (_shapeData.heightfieldData)
-			{
-				SAFE_DELETE_ARRAY(_shapeData.heightfieldData->heightData);
-				SAFE_DELETE(_shapeData.heightfieldData);
-			}
-			break;
-		}
-
-		// Free the bullet shape
-		SAFE_DELETE(_shape);
-	}
-}
-
-PhysicsCollisionShape::Type PhysicsCollisionShape::getType() const
-{
-	return _type;
-}
-
-PhysicsCollisionShape::Definition::Definition()
-	: isExplicit(false), centerAbsolute(false)
-{
-	memset(&data, 0, sizeof(data));
-}
-
-PhysicsCollisionShape::Definition::~Definition()
-{
-	switch (type)
-	{
-	case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
-		SAFE_RELEASE(data.heightfield);
-		break;
-
-	case PhysicsCollisionShape::SHAPE_MESH:
-		SAFE_RELEASE(data.mesh);
-		break;
-	}
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::box()
-{
-	Definition d;
-	d.type = SHAPE_BOX;
-	d.isExplicit = false;
-	d.centerAbsolute = false;
-	return d;
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::box(const Vector3& extents, const Vector3& center, bool absolute)
-{
-	Definition d;
-	d.type = SHAPE_BOX;
-	memcpy(d.data.box.extents, &extents.x, sizeof(float) * 3);
-	memcpy(d.data.box.center, &center.x, sizeof(float) * 3);
-	d.isExplicit = true;
-	d.centerAbsolute = absolute;
-	return d;
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere()
-{
-	Definition d;
-	d.type = SHAPE_SPHERE;
-	d.isExplicit = false;
-	d.centerAbsolute = false;
-	return d;
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere(float radius, const Vector3& center, bool absolute)
-{
-	Definition d;
-	d.type = SHAPE_SPHERE;
-	d.data.sphere.radius = radius;
-	memcpy(d.data.sphere.center, &center.x, sizeof(float) * 3);
-	d.isExplicit  = true;
-	d.centerAbsolute = absolute;
-	return d;
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule()
-{
-	Definition d;
-	d.type = SHAPE_CAPSULE;
-	d.isExplicit = false;
-	d.centerAbsolute = false;
-	return d;
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule(float radius, float height, const Vector3& center, bool absolute)
-{
-	Definition d;
-	d.type = SHAPE_CAPSULE;
-	d.data.capsule.radius = radius;
-	d.data.capsule.height = height;
-	memcpy(d.data.capsule.center, &center.x, sizeof(float) * 3);
-	d.isExplicit = true;
-	d.centerAbsolute = absolute;
-	return d;
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* image)
-{
-	image->addRef();
-
-	Definition d;
-	d.type = SHAPE_HEIGHTFIELD;
-	d.data.heightfield = image;
-	d.isExplicit = true;
-	d.centerAbsolute = false;
-	return d;
-}
-
-PhysicsCollisionShape::Definition PhysicsCollisionShape::mesh(Mesh* mesh)
-{
-	mesh->addRef();
-
-	Definition d;
-	d.type = SHAPE_MESH;
-	d.data.mesh = mesh;
-	d.isExplicit = true;
-	d.centerAbsolute = false;
-	return d;
-}
-
-}
+#include "Base.h"
+#include "PhysicsCollisionShape.h"
+#include "Node.h"
+#include "Properties.h"
+
+namespace gameplay
+{
+
+PhysicsCollisionShape::PhysicsCollisionShape(Type type, btCollisionShape* shape)
+    : _type(type), _shape(shape)
+{
+    memset(&_shapeData, 0, sizeof(_shapeData));
+}
+
+PhysicsCollisionShape::PhysicsCollisionShape(const PhysicsCollisionShape& copy)
+{
+	// hidden
+}
+
+PhysicsCollisionShape::~PhysicsCollisionShape()
+{
+    if (_shape)
+    {
+        // Cleanup shape-specific cached data
+        switch (_type)
+        {
+        case SHAPE_MESH:
+            if (_shapeData.meshData)
+            {
+                SAFE_DELETE_ARRAY(_shapeData.meshData->vertexData);
+                for (unsigned int i = 0; i < _shapeData.meshData->indexData.size(); i++)
+                {
+                    SAFE_DELETE_ARRAY(_shapeData.meshData->indexData[i]);
+                }
+                SAFE_DELETE(_shapeData.meshData);
+            }
+            break;
+        case SHAPE_HEIGHTFIELD:
+            if (_shapeData.heightfieldData)
+            {
+                SAFE_DELETE_ARRAY(_shapeData.heightfieldData->heightData);
+                SAFE_DELETE(_shapeData.heightfieldData);
+            }
+            break;
+        }
+
+        // Free the bullet shape
+        SAFE_DELETE(_shape);
+    }
+}
+
+PhysicsCollisionShape::Type PhysicsCollisionShape::getType() const
+{
+    return _type;
+}
+
+PhysicsCollisionShape::Definition::Definition()
+    : isExplicit(false), centerAbsolute(false)
+{
+    memset(&data, 0, sizeof(data));
+}
+
+PhysicsCollisionShape::Definition::Definition(const Definition& definition)
+{
+    // Bitwise-copy the definition object (equivalent to default copy constructor).
+    memcpy(this, &definition, sizeof(PhysicsCollisionShape::Definition));
+
+    // Handle the types that have reference-counted members.
+    switch (type)
+    {
+    case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+        data.heightfield->addRef();
+        break;
+
+    case PhysicsCollisionShape::SHAPE_MESH:
+        data.mesh->addRef();
+        break;
+    }
+}
+
+PhysicsCollisionShape::Definition::~Definition()
+{
+    switch (type)
+    {
+    case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+        SAFE_RELEASE(data.heightfield);
+        break;
+
+    case PhysicsCollisionShape::SHAPE_MESH:
+        SAFE_RELEASE(data.mesh);
+        break;
+    }
+}
+
+PhysicsCollisionShape::Definition& PhysicsCollisionShape::Definition::operator=(const Definition& definition)
+{
+    if (this != &definition)
+    {
+        // Bitwise-copy the definition object (equivalent to default copy constructor).
+        memcpy(this, &definition, sizeof(PhysicsCollisionShape::Definition));
+
+        // Handle the types that have reference-counted members.
+        switch (type)
+        {
+        case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+            data.heightfield->addRef();
+            break;
+
+        case PhysicsCollisionShape::SHAPE_MESH:
+            data.mesh->addRef();
+            break;
+        }
+    }
+
+    return *this;
+}
+
+PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Node* node, Properties* properties)
+{
+    // Check if the properties is valid and has a valid namespace.
+    assert(properties);
+    if (!properties || 
+        !(strcmp(properties->getNamespace(), "character") == 0 || 
+        strcmp(properties->getNamespace(), "ghost") == 0 || 
+        strcmp(properties->getNamespace(), "rigidbody") == 0))
+    {
+        WARN("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to \'character\', \'ghost\', or \'rigidbody\'.");
+        return NULL;
+    }
+
+    // Set values to their defaults.
+    PhysicsCollisionShape::Type type = PhysicsCollisionShape::SHAPE_BOX;
+    Vector3* extents = NULL;
+    Vector3* center = NULL;
+    float radius = -1.0f;
+    float height = -1.0f;
+    bool centerIsAbsolute = false;
+    const char* imagePath = NULL;
+    bool typeSpecified = false;
+
+    // Load the defined properties.
+    properties->rewind();
+    const char* name;
+    while (name = properties->getNextProperty())
+    {
+        if (strcmp(name, "type") == 0)
+        {
+            std::string typeStr = properties->getString();
+            if (typeStr == "BOX")
+                type = SHAPE_BOX;
+            else if (typeStr == "SPHERE")
+                type = SHAPE_SPHERE;
+            else if (typeStr == "MESH")
+                type = SHAPE_MESH;
+            else if (typeStr == "HEIGHTFIELD")
+                type = SHAPE_HEIGHTFIELD;
+            else if (typeStr == "CAPSULE")
+                type = SHAPE_CAPSULE;
+            else
+            {
+                WARN_VARG("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", typeStr.c_str());
+                return NULL;
+            }
+
+            typeSpecified = true;
+        }
+        else if (strcmp(name, "image") == 0)
+        {
+            imagePath = properties->getString();
+        }
+        else if (strcmp(name, "radius") == 0)
+        {
+            radius = properties->getFloat();
+        }
+        else if (strcmp(name, "height") == 0)
+        {
+            height = properties->getFloat();
+        }
+        else if (strcmp(name, "extents") == 0)
+        {
+            extents = new Vector3();
+            properties->getVector3("extents", extents);
+        }
+        else if (strcmp(name, "center") == 0)
+        {
+            center = new Vector3();
+            properties->getVector3("center", center);
+        }
+        else if (strcmp(name, "center-absolute") == 0)
+        {
+            centerIsAbsolute = properties->getBool();
+        }
+    }
+
+    if (!typeSpecified)
+    {
+        WARN("Missing 'type' specifier for collision shape definition.");
+        return NULL;
+    }
+
+    // Create the collision shape.
+    PhysicsCollisionShape::Definition* shape = new PhysicsCollisionShape::Definition();
+    switch (type)
+    {
+        case SHAPE_BOX:
+            if (extents)
+            {
+                if (center)
+                {
+                    *shape = box(*extents, *center, centerIsAbsolute);
+                }
+                else
+                {
+                    *shape = box(*extents);
+                }
+            }
+            else
+            {
+                *shape = box();
+            }
+            break;
+        case SHAPE_SPHERE:
+            if (radius != -1.0f)
+            {
+                if (center)
+                {
+                    *shape = sphere(radius, *center, centerIsAbsolute);
+                }
+                else
+                {
+                    *shape = sphere(radius);
+                }
+            }
+            else
+            {
+                *shape = sphere();
+            }
+            break;
+        case SHAPE_CAPSULE:
+            if (radius != -1.0f && height != -1.0f)
+            {
+                if (center)
+                {
+                    *shape = capsule(radius, height, *center, centerIsAbsolute);
+                }
+                else
+                {
+                    *shape = capsule(radius, height);
+                }
+            }
+            else
+            {
+                *shape = capsule();
+            }
+            break;
+        case SHAPE_MESH:
+        {
+            // Mesh is required on node
+            Mesh* nodeMesh = node->getModel() ? node->getModel()->getMesh() : NULL;
+            if (nodeMesh == NULL)
+            {
+                WARN("Cannot create mesh rigid body for node without mode/mesh.");
+                return NULL;
+            }
+
+            // Check that the node's mesh's primitive type is supported.
+            switch (nodeMesh->getPrimitiveType())
+            {
+                case Mesh::TRIANGLES:
+                {
+                    *shape = mesh(nodeMesh);
+                    break;
+                }
+                case Mesh::LINES:
+                case Mesh::LINE_STRIP:
+                case Mesh::POINTS:
+                case Mesh::TRIANGLE_STRIP:
+                    WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
+                    SAFE_DELETE(shape);
+                    break;
+            }
+
+            break;
+        }
+        case SHAPE_HEIGHTFIELD:
+            if (imagePath == NULL)
+            {
+                WARN("Heightfield rigid body requires an image path.");
+            }
+            else
+            {
+                // Load the image data from the given file path.
+                Image* image = Image::create(imagePath);
+                if (!image)
+                    return NULL;
+
+                // Ensure that the image's pixel format is supported.
+                switch (image->getFormat())
+                {
+                    case Image::RGB:
+                    case Image::RGBA:
+                        break;
+                    default:
+                        WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
+                        return NULL;
+                }
+
+                *shape = PhysicsCollisionShape::heightfield(image);
+                SAFE_RELEASE(image);
+            }
+            break;
+        default:
+            WARN("Unsupported value for physics collision shape type.");
+            break;
+    }
+
+    SAFE_DELETE(extents);
+    SAFE_DELETE(center);
+
+    return shape;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::box()
+{
+    Definition d;
+    d.type = SHAPE_BOX;
+    d.isExplicit = false;
+    d.centerAbsolute = false;
+    return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::box(const Vector3& extents, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_BOX;
+	memcpy(d.data.box.extents, &extents.x, sizeof(float) * 3);
+	memcpy(d.data.box.center, &center.x, sizeof(float) * 3);
+	d.isExplicit = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere()
+{
+    Definition d;
+    d.type = SHAPE_SPHERE;
+    d.isExplicit = false;
+    d.centerAbsolute = false;
+    return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere(float radius, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_SPHERE;
+	d.data.sphere.radius = radius;
+	memcpy(d.data.sphere.center, &center.x, sizeof(float) * 3);
+	d.isExplicit  = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule()
+{
+    Definition d;
+    d.type = SHAPE_CAPSULE;
+    d.isExplicit = false;
+    d.centerAbsolute = false;
+    return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule(float radius, float height, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_CAPSULE;
+	d.data.capsule.radius = radius;
+	d.data.capsule.height = height;
+	memcpy(d.data.capsule.center, &center.x, sizeof(float) * 3);
+	d.isExplicit = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* image)
+{
+    image->addRef();
+
+    Definition d;
+    d.type = SHAPE_HEIGHTFIELD;
+    d.data.heightfield = image;
+    d.isExplicit = true;
+    d.centerAbsolute = false;
+    return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::mesh(Mesh* mesh)
+{
+    mesh->addRef();
+
+    Definition d;
+    d.type = SHAPE_MESH;
+    d.data.mesh = mesh;
+    d.isExplicit = true;
+    d.centerAbsolute = false;
+    return d;
+}
+
+}

+ 235 - 219
gameplay/src/PhysicsCollisionShape.h

@@ -1,219 +1,235 @@
-#ifndef PHYSICSCOLLISIONSHAPE_H_
-#define PHYSICSCOLLISIONSHAPE_H_
-
-#include "Vector3.h"
-#include "Image.h"
-#include "Mesh.h"
-
-namespace gameplay
-{
-
-/**
- * Base physics collision shape class that all supported shapes derive from.
- */
-class PhysicsCollisionShape : public Ref
-{
-	friend class PhysicsController;
-	friend class PhysicsRigidBody;
-
-public:
-
-	/**
-	 * Defines the supported collision shape types.
-	 */
-	enum Type
-	{
-		SHAPE_BOX,
-		SHAPE_SPHERE,
-		SHAPE_CAPSULE,
-		SHAPE_MESH,
-		SHAPE_HEIGHTFIELD
-	};
-
-	/**
-	 * Structure representing the definition of a collision shape, which is used
-	 * during collision shape construction time.
-	 *
-	 * Use the static methods on the PhysicsCollisionShape class to return
-	 * 
-	 */
-	struct Definition
-	{
-		friend class PhysicsCollisionShape;
-		friend class PhysicsController;
-		friend class PhysicsRigidBody;
-
-	public:
-
-		~Definition();
-
-	private:
-
-		Definition();
-
-		// Shape type.
-		PhysicsCollisionShape::Type type;
-
-		// Shape data.
-		struct BoxData { float center[3], extents[3]; };
-		struct SphereData { float center[3]; float radius; };
-		struct CapsuleData { float center[3]; float radius, height; };
-
-		union
-		{
-			BoxData box;
-			SphereData sphere;
-			CapsuleData capsule;
-			Image* heightfield;
-			Mesh* mesh;
-		} data;
-
-		// Whether the shape definition is explicit, or if it is inherited from node bounds.
-		bool isExplicit;
-
-		// Whether the center position is absolute or relative to the node position.
-		bool centerAbsolute;
-	};
-
-	/**
-	 * Returns the type of this collision shape.
-	 *
-	 * @return The collision shape type.
-	 */
-	PhysicsCollisionShape::Type getType() const;
-
-	/**
-	 * Returns the internal bullet physics shape object.
-	 *
-	 * @return The bullet shape object.
-	 */
-	btCollisionShape* getShape() const
-	{
-		return _shape;
-	}
-
-	/**
-	 * Defines a box shape, using the bounding volume of the node it is attached to.
-	 *
-	 * @return Definition of a box shape.
-	 */
-	static PhysicsCollisionShape::Definition box();
-
-	/**
-	 * Defines a box shape, using the specified shape information and center.
-	 *
-	 * @param extents Extents of the box shape along the x, y and z axes.
-	 * @param center Center point of the box.
-	 * @param absolute True to specifiy that the given center point is an absolute position.
-	 *		By default the center point is treated as relative to the location of the node
-	 *		that the shape is attached to.
-	 *
-	 * @return Definition of a box shape.
-	 */
-	static PhysicsCollisionShape::Definition box(const Vector3& extents, const Vector3& center = Vector3::zero(), bool absolute = false);
-
-	/**
-	 * Defines a sphere shape, using the bounding volume of the node it is attached to.
-	 *
-	 * @return Definition of a sphere shape.
-	 */
-	static PhysicsCollisionShape::Definition sphere();
-
-	/**
-	 * Defines a sphere shape, using the specified shape information and center.
-	 *
-	 * @param radius Radius of the sphere.
-	 * @param center Center point of the sphere.
-	 * @param absolute True to specifiy that the given center point is an absolute position.
-	 *		By default the center point is treated as relative to the location of the node
-	 *		that the shape is attached to.
-	 *
-	 * @return Definition of a sphere shape.
-	 */
-	static PhysicsCollisionShape::Definition sphere(float radius, const Vector3& center = Vector3::zero(), bool absolute = false);
-
-	/**
-	 * Defines a capsule shape, using the bounding volume of the node it is attached to.
-	 *
-	 * @return Definition of a capsule shape.
-	 */
-	static PhysicsCollisionShape::Definition capsule();
-
-	/**
-	 * Defines a capsule shape, using the specified shape information and center.
-	 *
-	 * @param radius Radius of the capsule.
-	 * @param height Height of the capsule.
-	 * @param center Center point of the capsule.
-	 * @param absolute True to specifiy that the given center point is an absolute position.
-	 *		By default the center point is treated as relative to the location of the node
-	 *		that the shape is attached to.
-	 *
-	 * @return Definition of a capsule shape.
-	 */
-	static PhysicsCollisionShape::Definition capsule(float radius, float height, const Vector3& center = Vector3::zero(), bool absolute = false);
-
-	/**
-	 * Defines a heightfield shape using the specified heightfield image.
-	 *
-	 * @return Definition of a heightfield shape.
-	 */
-	static PhysicsCollisionShape::Definition heightfield(Image* image);
-
-	/**
-	 * Defines a mesh shape using the specified mehs.
-	 *
-	 * @return Definition of a mesh shape.
-	 */
-	static PhysicsCollisionShape::Definition mesh(Mesh* mesh);
-
-private:
-
-	struct MeshData
-	{
-		float* vertexData;
-		std::vector<unsigned char*> indexData;
-	};
-
-	struct HeightfieldData
-	{
-		float* heightData;
-		unsigned int width;
-		unsigned int height;
-		mutable Matrix inverse;
-		mutable bool inverseIsDirty;
-	};
-
-	/**
-	 * Constructor.
-	 */
-	PhysicsCollisionShape(Type type, btCollisionShape* shape);
-
-	/** 
-	 * Hidden copy constructor.
-	 */
-	PhysicsCollisionShape(const PhysicsCollisionShape& copy);
-
-	/**
-	 * Destructor.
-	 */
-	~PhysicsCollisionShape();
-
-	// Shape type
-	Type _type;
-
-	// Bullet shape object
-	btCollisionShape* _shape;
-
-	// Shape specific cached data
-	union
-	{
-		MeshData* meshData;
-		HeightfieldData* heightfieldData;
-	} _shapeData;
-
-};
-
-}
-
-#endif
+#ifndef PHYSICSCOLLISIONSHAPE_H_
+#define PHYSICSCOLLISIONSHAPE_H_
+
+#include "Vector3.h"
+#include "Image.h"
+#include "Mesh.h"
+
+namespace gameplay
+{
+    class Node;
+    class Properties;
+
+/**
+ * Base physics collision shape class that all supported shapes derive from.
+ */
+class PhysicsCollisionShape : public Ref
+{
+    friend class PhysicsController;
+    friend class PhysicsRigidBody;
+
+public:
+
+    /**
+     * Defines the supported collision shape types.
+     */
+    enum Type
+    {
+        SHAPE_BOX,
+        SHAPE_SPHERE,
+        SHAPE_CAPSULE,
+        SHAPE_MESH,
+        SHAPE_HEIGHTFIELD
+    };
+
+    /**
+     * Structure representing the definition of a collision shape, which is used
+     * during collision shape construction time.
+     *
+     * Use the static methods on the PhysicsCollisionShape class to return
+     * 
+     */
+    struct Definition
+    {
+        friend class PhysicsCollisionShape;
+        friend class PhysicsController;
+        friend class PhysicsRigidBody;
+        friend class PhysicsCharacter;
+        friend class PhysicsGhostObject;
+
+    public:
+
+        ~Definition();
+
+    private:
+
+        Definition();
+        Definition(const Definition& definition);
+        Definition& operator=(const Definition& definition);
+
+        /**
+         * Creates a PhysicsCollisionShape#Definition object from the given properties object (for the given node).
+         * 
+         * @param node The node to create the PhysicsCollisionShape::Definition object for.
+         * @param properties The properties object to create the PhysicsCollisionShape#Definition object from.
+         * @return A PhysicsCollisionShape#Definition object.
+         */
+        static Definition* create(Node* node, Properties* properties);
+
+        // Shape type.
+        PhysicsCollisionShape::Type type;
+
+		// Shape data.
+		struct BoxData { float center[3], extents[3]; };
+		struct SphereData { float center[3]; float radius; };
+		struct CapsuleData { float center[3]; float radius, height; };
+
+        union
+        {
+			BoxData box;
+			SphereData sphere;
+			CapsuleData capsule;
+			Image* heightfield;
+			Mesh* mesh;
+		} data;
+
+        // Whether the shape definition is explicit, or if it is inherited from node bounds.
+        bool isExplicit;
+
+        // Whether the center position is absolute or relative to the node position.
+        bool centerAbsolute;
+    };
+
+    /**
+     * Returns the type of this collision shape.
+     *
+     * @return The collision shape type.
+     */
+    PhysicsCollisionShape::Type getType() const;
+
+	/**
+	 * Returns the internal bullet physics shape object.
+	 *
+	 * @return The bullet shape object.
+	 */
+	btCollisionShape* getShape() const
+	{
+		return _shape;
+	}
+
+    /**
+     * Defines a box shape, using the bounding volume of the node it is attached to.
+     *
+     * @return Definition of a box shape.
+     */
+    static PhysicsCollisionShape::Definition box();
+
+    /**
+     * Defines a box shape, using the specified shape information and center.
+     *
+     * @param extents Extents of the box shape along the x, y and z axes.
+     * @param center Center point of the box.
+     * @param absolute True to specifiy that the given center point is an absolute position.
+     *		By default the center point is treated as relative to the location of the node
+     *		that the shape is attached to.
+     *
+     * @return Definition of a box shape.
+     */
+    static PhysicsCollisionShape::Definition box(const Vector3& extents, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+    /**
+     * Defines a sphere shape, using the bounding volume of the node it is attached to.
+     *
+     * @return Definition of a sphere shape.
+     */
+    static PhysicsCollisionShape::Definition sphere();
+
+    /**
+     * Defines a sphere shape, using the specified shape information and center.
+     *
+     * @param radius Radius of the sphere.
+     * @param center Center point of the sphere.
+     * @param absolute True to specifiy that the given center point is an absolute position.
+     *		By default the center point is treated as relative to the location of the node
+     *		that the shape is attached to.
+     *
+     * @return Definition of a sphere shape.
+     */
+    static PhysicsCollisionShape::Definition sphere(float radius, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+    /**
+     * Defines a capsule shape, using the bounding volume of the node it is attached to.
+     *
+     * @return Definition of a capsule shape.
+     */
+    static PhysicsCollisionShape::Definition capsule();
+
+    /**
+     * Defines a capsule shape, using the specified shape information and center.
+     *
+     * @param radius Radius of the capsule.
+     * @param height Height of the capsule.
+     * @param center Center point of the capsule.
+     * @param absolute True to specifiy that the given center point is an absolute position.
+     *		By default the center point is treated as relative to the location of the node
+     *		that the shape is attached to.
+     *
+     * @return Definition of a capsule shape.
+     */
+    static PhysicsCollisionShape::Definition capsule(float radius, float height, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+    /**
+     * Defines a heightfield shape using the specified heightfield image.
+     *
+     * @return Definition of a heightfield shape.
+     */
+    static PhysicsCollisionShape::Definition heightfield(Image* image);
+
+    /**
+     * Defines a mesh shape using the specified mehs.
+     *
+     * @return Definition of a mesh shape.
+     */
+    static PhysicsCollisionShape::Definition mesh(Mesh* mesh);
+
+private:
+
+    struct MeshData
+    {
+        float* vertexData;
+        std::vector<unsigned char*> indexData;
+    };
+
+    struct HeightfieldData
+    {
+        float* heightData;
+        unsigned int width;
+        unsigned int height;
+        mutable Matrix inverse;
+        mutable bool inverseIsDirty;
+    };
+
+    /**
+     * Constructor.
+     */
+    PhysicsCollisionShape(Type type, btCollisionShape* shape);
+
+	/** 
+	 * Hidden copy constructor.
+	 */
+	PhysicsCollisionShape(const PhysicsCollisionShape& copy);
+
+	/**
+	 * Destructor.
+	 */
+	~PhysicsCollisionShape();
+
+
+    // Shape type
+    Type _type;
+
+    // Bullet shape object
+    btCollisionShape* _shape;
+
+    // Shape specific cached data
+    union
+    {
+        MeshData* meshData;
+        HeightfieldData* heightfieldData;
+    } _shapeData;
+
+};
+
+}
+
+#endif

+ 31 - 6
gameplay/src/PhysicsGhostObject.cpp

@@ -9,35 +9,60 @@ namespace gameplay
 PhysicsGhostObject::PhysicsGhostObject(Node* node, const PhysicsCollisionShape::Definition& shape)
     : PhysicsCollisionObject(node), _ghostObject(NULL)
 {
-	Vector3 centerOfMassOffset;
+    Vector3 centerOfMassOffset;
     PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
 
     // Create and set the collision shape for the ghost object.
-	_collisionShape = physicsController->createShape(node, shape, &centerOfMassOffset);
+    _collisionShape = physicsController->createShape(node, shape, &centerOfMassOffset);
 
-	// Create the ghost object.
+    // Create the ghost object.
     _ghostObject = bullet_new<btPairCachingGhostObject>();
 	_ghostObject->setCollisionShape(_collisionShape->getShape());
 
     // Initialize a physics motion state object for syncing the transform.
-	_motionState = new PhysicsMotionState(_node, &centerOfMassOffset);
+    _motionState = new PhysicsMotionState(_node, &centerOfMassOffset);
     _motionState->getWorldTransform(_ghostObject->getWorldTransform());
 
     // Add the ghost object to the physics world.
     physicsController->addCollisionObject(this);
 
-	_node->addListener(this);
+    _node->addListener(this);
 }
 
 PhysicsGhostObject::~PhysicsGhostObject()
 {
-	_node->removeListener(this);
+    _node->removeListener(this);
 
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
     SAFE_DELETE(_ghostObject);
 }
 
+PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
+{
+    // Check if the properties is valid and has a valid namespace.
+    assert(properties);
+    if (!properties || !(strcmp(properties->getNamespace(), "ghost") == 0))
+    {
+        WARN("Failed to load ghost object from properties object: must be non-null object and have namespace equal to \'ghost\'.");
+        return NULL;
+    }
+
+    // Load the physics collision shape definition.
+    PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
+    if (shape == NULL)
+    {
+        WARN("Failed to create collision shape during ghost object creation.");
+        return NULL;
+    }
+
+    // Create the ghost object.
+    PhysicsGhostObject* ghost = new PhysicsGhostObject(node, *shape);
+    SAFE_DELETE(shape);
+
+    return ghost;
+}
+
 PhysicsCollisionObject::Type PhysicsGhostObject::getType() const
 {
     return GHOST_OBJECT;

+ 12 - 2
gameplay/src/PhysicsGhostObject.h

@@ -24,7 +24,7 @@ public:
      */
     PhysicsCollisionObject::Type getType() const;
 
-	/**
+    /**
      * Used to synchronize the transform between GamePlay and Bullet.
      */
     void transformChanged(Transform* transform, long cookie);
@@ -44,13 +44,23 @@ protected:
      * @param node The node to attach the ghost object to.
      * @param shape The collision shape definition for the ghost object.
      */
-	PhysicsGhostObject(Node* node, const PhysicsCollisionShape::Definition& shape);
+    PhysicsGhostObject(Node* node, const PhysicsCollisionShape::Definition& shape);
 
     /**
      * Destructor.
      */
     virtual ~PhysicsGhostObject();
 
+    /**
+     * Creates a ghost object from the specified properties object.
+     * 
+     * @param node The node to create a ghost object for; note that the node must have
+     *      a model attached to it prior to creating a ghost object for it.
+     * @param properties The properties object defining the ghost object (must have namespace equal to 'ghost').
+     * @return The newly created ghost object, or <code>NULL</code> if the ghost object failed to load.
+     */
+    static PhysicsGhostObject* create(Node* node, Properties* properties);
+
     btPairCachingGhostObject* _ghostObject;
 };
 

+ 73 - 235
gameplay/src/PhysicsRigidBody.cpp

@@ -13,12 +13,12 @@ namespace gameplay
 PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters)
         : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL)
 {
-	// Create our collision sh ape
-	Vector3 centerOfMassOffset;
-	_collisionShape = Game::getInstance()->getPhysicsController()->createShape(node, shape, &centerOfMassOffset);
+    // Create our collision shape
+    Vector3 centerOfMassOffset;
+    _collisionShape = Game::getInstance()->getPhysicsController()->createShape(node, shape, &centerOfMassOffset);
 
-	// Create motion state object
-	_motionState = new PhysicsMotionState(node, (centerOfMassOffset.lengthSquared() > MATH_EPSILON) ? &centerOfMassOffset : NULL);
+    // Create motion state object
+    _motionState = new PhysicsMotionState(node, (centerOfMassOffset.lengthSquared() > MATH_EPSILON) ? &centerOfMassOffset : NULL);
 
     // If the mass is non-zero, then the object is dynamic so we calculate the local 
     // inertia. However, if the collision shape is a triangle mesh, we don't calculate 
@@ -34,48 +34,48 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Defi
     rbInfo.m_linearDamping = parameters.linearDamping;
     rbInfo.m_angularDamping = parameters.angularDamping;
 
-	// Create + assign the new bullet rigid body object.
-	_body = bullet_new<btRigidBody>(rbInfo);
+    // Create + assign the new bullet rigid body object.
+    _body = bullet_new<btRigidBody>(rbInfo);
 
-	// Set other initially defined properties.
+    // Set other initially defined properties.
     setKinematic(parameters.kinematic);
-	setAnisotropicFriction(parameters.anisotropicFriction);
-	setGravity(parameters.gravity);
+    setAnisotropicFriction(parameters.anisotropicFriction);
+    setGravity(parameters.gravity);
 
     // Add ourself to the physics world.
     Game::getInstance()->getPhysicsController()->addCollisionObject(this);
 
-	if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
-	{
-		// Add a listener on the node's transform so we can track dirty changes to calculate
-		// an inverse matrix for transforming heightfield points between world and local space.
-		_node->addListener(this);
-	}
+    if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+    {
+        // Add a listener on the node's transform so we can track dirty changes to calculate
+        // an inverse matrix for transforming heightfield points between world and local space.
+        _node->addListener(this);
+    }
 }
 
 PhysicsRigidBody::~PhysicsRigidBody()
 {
-	// Clean up all constraints linked to this rigid body.
-	if (_constraints)
-	{
-		for (unsigned int i = 0; i < _constraints->size(); ++i)
-		{
-			SAFE_DELETE((*_constraints)[i]);
-		}
-		SAFE_DELETE(_constraints);
-	}
-
-	// Remove collision object from physics controller
+    // Clean up all constraints linked to this rigid body.
+    if (_constraints)
+    {
+        for (unsigned int i = 0; i < _constraints->size(); ++i)
+        {
+            SAFE_DELETE((*_constraints)[i]);
+        }
+        SAFE_DELETE(_constraints);
+    }
+
+    // Remove collision object from physics controller
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
     // Clean up the rigid body and its related objects.
-	SAFE_DELETE(_body);
+    SAFE_DELETE(_body);
 
-	// Unregister node listener (only registered for heihgtfield collision shape types)
-	if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
-	{
-		_node->removeListener(this);
-	}
+    // Unregister node listener (only registered for heihgtfield collision shape types)
+    if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+    {
+        _node->removeListener(this);
+    }
 }
 
 PhysicsCollisionObject::Type PhysicsRigidBody::getType() const
@@ -141,25 +141,6 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
     }
 }
 
-PhysicsRigidBody* PhysicsRigidBody::create(Node* node, const char* filePath)
-{
-    assert(filePath);
-
-    // Load the rigid body properties from file.
-    Properties* properties = Properties::create(filePath);
-    assert(properties);
-    if (properties == NULL)
-    {
-        WARN_VARG("Failed to load rigid body file: %s", filePath);
-        return NULL;
-    }
-
-    PhysicsRigidBody* body = create(node, properties->getNextNamespace());
-    SAFE_DELETE(properties);
-
-    return body;
-}
-
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
@@ -170,41 +151,23 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         return NULL;
     }
 
-    // Set values to their defaults.
-	bool typeSpecified = false;
-	PhysicsCollisionShape::Type type;
-	Parameters parameters;
-    const char* imagePath = NULL;
-    float radius, height;
-	Vector3 center, min, max;
-	int bits = 0;
+    // Load the physics collision shape definition.
+    PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
+    if (shape == NULL)
+    {
+        WARN("Failed to create collision shape during rigid body creation.");
+        return NULL;
+    }
+
+    // Set the rigid body parameters to their defaults.
+    Parameters parameters;
 
-    // Load the defined properties.
+    // Load the defined rigid body parameters.
     properties->rewind();
     const char* name;
     while ((name = properties->getNextProperty()) != NULL)
     {
-        if (strcmp(name, "type") == 0)
-        {
-            std::string typeStr = properties->getString();
-            if (typeStr == "BOX")
-                type = PhysicsCollisionShape::SHAPE_BOX;
-            else if (typeStr == "SPHERE")
-                type = PhysicsCollisionShape::SHAPE_SPHERE;
-			else if (typeStr == "CAPSULE")
-                type = PhysicsCollisionShape::SHAPE_CAPSULE;
-			else if (typeStr == "HEIGHTFIELD")
-                type = PhysicsCollisionShape::SHAPE_HEIGHTFIELD;
-            else if (typeStr == "MESH")
-                type = PhysicsCollisionShape::SHAPE_MESH;
-            else
-            {
-                WARN_VARG("Could not create rigid body; unsupported value for rigid body type: '%s'.", typeStr.c_str());
-                return NULL;
-            }
-			typeSpecified = true;
-        }
-        else if (strcmp(name, "mass") == 0)
+        if (strcmp(name, "mass") == 0)
         {
             parameters.mass = properties->getFloat();
         }
@@ -236,136 +199,11 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         {
             properties->getVector3(NULL, &parameters.gravity);
         }
-        else if (strcmp(name, "image") == 0)
-        {
-            imagePath = properties->getString();
-        }
-        else if (strcmp(name, "radius") == 0)
-        {
-			radius = properties->getFloat();
-			bits |= 1;
-        }
-        else if (strcmp(name, "height") == 0)
-        {
-            height = properties->getFloat();
-			bits |= 2;
-        }
-		else if (strcmp(name, "center") == 0)
-		{
-			properties->getVector3(NULL, &center);
-			bits |= 4;
-		}
-		else if (strcmp(name, "min") == 0)
-		{
-			properties->getVector3(NULL, &min);
-			bits |= 8;
-		}
-		else if (strcmp(name, "max") == 0)
-		{
-			properties->getVector3(NULL, &max);
-			bits |= 16;
-		}
     }
 
-	if (!typeSpecified)
-	{
-		WARN("Missing 'type' specifier for rigid body definition.");
-		return NULL;
-	}
-
-	PhysicsRigidBody* body = NULL;
-
-	switch (type)
-	{
-	case PhysicsCollisionShape::SHAPE_BOX:
-		if ((bits & 8/*min*/) || (bits & 16/*max*/))
-		{
-			// Explicitly defined box shape
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::box(min, max), parameters);
-		}
-		else
-		{
-			// Auto box shape
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::box(), parameters);
-		}
-		break;
-
-	case PhysicsCollisionShape::SHAPE_SPHERE:
-		if ((bits & 4/*center*/) || (bits & 1/*radius*/))
-		{
-			// Explicitly defined sphere shape
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::sphere(radius, center), parameters);
-		}
-		else
-		{
-			// Auto sphere shape
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::sphere(), parameters);
-		}
-		break;
-
-    case PhysicsCollisionShape::SHAPE_CAPSULE:
-		if ((bits & 1/*radius*/) || (bits & 2/*height*/))
-		{
-			// Explicitly defined capsule shape
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::capsule(radius, height, center), parameters);
-		}
-		else
-		{
-			// Auto capsule shape
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::capsule(), parameters);
-		}
-        break;
-
-    case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
-		{
-			if (imagePath == NULL)
-			{
-				WARN("Heightfield rigid body requires an image path.");
-				return NULL;
-			}
-
-            // Load the image data from the given file path.
-            Image* image = Image::create(imagePath);
-            if (!image)
-			{
-				WARN_VARG("Failed to load heightmap image: %s", imagePath);
-                return NULL;
-			}
-
-            // Ensure that the image's pixel format is supported.
-            switch (image->getFormat())
-            {
-                case Image::RGB:
-                case Image::RGBA:
-                    break;
-                default:
-                    WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
-                    return NULL;
-            }
-
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::heightfield(image), parameters);
-
-			SAFE_RELEASE(image);
-		}
-        break;
-
-	case PhysicsCollisionShape::SHAPE_MESH:
-		{
-			// Mesh is required on node
-			Mesh* mesh = node->getModel() ? node->getModel()->getMesh() : NULL;
-			if (mesh == NULL)
-			{
-				WARN("Cannot create mesh rigid body for node without mode/mesh.");
-				return NULL;
-			}
-
-			body = new PhysicsRigidBody(node, PhysicsCollisionShape::mesh(mesh), parameters);
-		}
-		break;
-
-	default:
-		break;
-	}
+    // Create the rigid body.
+    PhysicsRigidBody* body = new PhysicsRigidBody(node, *shape, parameters);
+    SAFE_DELETE(shape);
 
     return body;
 }
@@ -387,21 +225,21 @@ void PhysicsRigidBody::setKinematic(bool kinematic)
 float PhysicsRigidBody::getHeight(float x, float y) const
 {
     // This function is only supported for heightfield rigid bodies.
-	if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+    if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
         WARN("Attempting to get the height of a non-heightfield rigid body.");
         return 0.0f;
     }
 
     // Calculate the correct x, y position relative to the heightfield data.
-	if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
+    if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
     {
-		_node->getWorldMatrix().invert(&_collisionShape->_shapeData.heightfieldData->inverse);
-		_collisionShape->_shapeData.heightfieldData->inverseIsDirty = false;
+        _node->getWorldMatrix().invert(&_collisionShape->_shapeData.heightfieldData->inverse);
+        _collisionShape->_shapeData.heightfieldData->inverseIsDirty = false;
     }
 
-	float w = _collisionShape->_shapeData.heightfieldData->width;
-	float h = _collisionShape->_shapeData.heightfieldData->height;
+    float w = _collisionShape->_shapeData.heightfieldData->width;
+    float h = _collisionShape->_shapeData.heightfieldData->height;
 
     Vector3 v = _collisionShape->_shapeData.heightfieldData->inverse * Vector3(x, 0.0f, y);
     x = (v.x + (0.5f * (w - 1))) * w / (w - 1);
@@ -414,43 +252,43 @@ float PhysicsRigidBody::getHeight(float x, float y) const
         return 0.0f;
     }
 
-	return PhysicsController::calculateHeight(_collisionShape->_shapeData.heightfieldData->heightData, w, h, x, y);
+    return PhysicsController::calculateHeight(_collisionShape->_shapeData.heightfieldData->heightData, w, h, x, y);
 }
 
 void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 {
-	if (_constraints == NULL)
-		_constraints = new std::vector<PhysicsConstraint*>();
+    if (_constraints == NULL)
+        _constraints = new std::vector<PhysicsConstraint*>();
 
     _constraints->push_back(constraint);
 }
 
 void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
 {
-	if (_constraints)
-	{
-		for (unsigned int i = 0; i < _constraints->size(); ++i)
-		{
-			if ((*_constraints)[i] == constraint)
-			{
-				_constraints->erase(_constraints->begin() + i);
-				break;
-			}
-		}
-	}
+    if (_constraints)
+    {
+        for (unsigned int i = 0; i < _constraints->size(); ++i)
+        {
+            if ((*_constraints)[i] == constraint)
+            {
+                _constraints->erase(_constraints->begin() + i);
+                break;
+            }
+        }
+    }
 }
 
 bool PhysicsRigidBody::supportsConstraints()
 {
-	return (getShapeType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD && getShapeType() != PhysicsCollisionShape::SHAPE_MESH);
+    return (getShapeType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD && getShapeType() != PhysicsCollisionShape::SHAPE_MESH);
 }
 
 void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
 {
-	if (getShapeType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
-	{
-		_collisionShape->_shapeData.heightfieldData->inverseIsDirty = true;
-	}
+    if (getShapeType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+    {
+        _collisionShape->_shapeData.heightfieldData->inverseIsDirty = true;
+    }
 }
 
 }

+ 0 - 10
gameplay/src/PhysicsRigidBody.h

@@ -289,16 +289,6 @@ private:
      */
     PhysicsRigidBody(const PhysicsRigidBody& body);
 
-    /**
-     * Creates a rigid body from the rigid body file at the given path.
-     * 
-     * @param node The node to create a rigid body for; note that the node must have
-     *      a model attached to it prior to creating a rigid body for it.
-     * @param filePath The path to the rigid body file.
-     * @return The rigid body or <code>NULL</code> if the rigid body could not be loaded.
-     */
-    static PhysicsRigidBody* create(Node* node, const char* filePath);
-
     /**
      * Creates a rigid body from the specified properties object.
      * 

+ 56 - 37
gameplay/src/RadioButton.cpp

@@ -29,7 +29,7 @@ RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
     RadioButton* radioButton = new RadioButton();
     radioButton->init(style, properties);
 
-    properties->getVector2("iconSize", &radioButton->_iconSize);
+    properties->getVector2("imageSize", &radioButton->_imageSize);
 
     if (properties->getBool("selected"))
     {
@@ -48,21 +48,14 @@ RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
     return radioButton;
 }
 
-void RadioButton::setIconSize(float width, float height)
+void RadioButton::setImageSize(float width, float height)
 {
-    _iconSize.set(width, height);
+    _imageSize.set(width, height);
 }
 
-const Vector2& RadioButton::getIconSize() const
+const Vector2& RadioButton::getImageSize() const
 {
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    if (_iconSize.isZero() && icon)
-    {
-        return icon->getSize();
-    }
-
-    return _iconSize;
+    return _imageSize;
 }
 
 void RadioButton::addListener(Control::Listener* listener, int eventFlags)
@@ -121,42 +114,57 @@ void RadioButton::clearSelected(const std::string& groupId)
     }
 }
 
-void RadioButton::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // Left, v-center.
-    // TODO: Set an alignment for icons.
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getRadioButtonIcon();
-    if (icon)
+    // TODO: Set an alignment for radio button images.
+    const Theme::Border border = getBorder(_state);
+    const Theme::Padding padding = _style->getPadding();
+    float opacity = getOpacity(_state);
+
+    if (_selected)
     {
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
+        const Rectangle& selectedRegion = getImageRegion("selected", _state);
+        const Theme::UVs& selected = getImageUVs("selected", _state);
+        Vector4 selectedColor = getImageColor("selected", _state);
+        selectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-            border = containerRegion->getBorder();
+            size.set(selectedRegion.width, selectedRegion.height);
         }
-        const Theme::Padding padding = _style->getPadding();
-
-        Vector2& size = _iconSize;
-        if (_iconSize.isZero())
+        else
         {
-            size = icon->getSize();
+            size.set(_imageSize);
         }
-        const Vector4 color = icon->getColor();
 
         Vector2 pos(clip.x + _position.x + border.left + padding.left,
             clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
-        if (_selected)
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
+    }
+    else
+    {
+        const Rectangle& unselectedRegion = getImageRegion("unselected", _state);
+        const Theme::UVs& unselected = getImageUVs("unselected", _state);
+        Vector4 unselectedColor = getImageColor("unselected", _state);
+        unselectedColor.w *= opacity;
+
+        Vector2 size;
+        if (_imageSize.isZero())
         {
-            const Theme::UVs on = icon->getOnUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, on.u1, on.v1, on.u2, on.v2, color);
+            size.set(unselectedRegion.width, unselectedRegion.height);
         }
         else
         {
-            const Theme::UVs off = icon->getOffUVs();
-            spriteBatch->draw(pos.x, pos.y, size.x, size.y, off.u1, off.v1, off.u2, off.v2, color);
+            size.set(_imageSize);
         }
+
+        Vector2 pos(clip.x + _position.x + border.left + padding.left,
+            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+
+        spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
     }
 }
 
@@ -164,12 +172,23 @@ void RadioButton::update(const Rectangle& clip)
 {
     Control::update(clip);
 
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Icon* icon = overlay->getCheckBoxIcon();
-    Vector2& size = _iconSize;
-    if (_iconSize.isZero() && icon)
+    Vector2 size;
+    if (_imageSize.isZero())
+    {
+        if (_selected)
+        {
+            const Rectangle& selectedRegion = getImageRegion("selected", _state);
+            size.set(selectedRegion.width, selectedRegion.height);
+        }
+        else
+        {
+            const Rectangle& unselectedRegion = getImageRegion("unselected", _state);
+            size.set(unselectedRegion.width, unselectedRegion.height);
+        }
+    }
+    else
     {
-        size = icon->getSize();
+        size.set(_imageSize);
     }
     float iconWidth = size.x;
 

+ 4 - 4
gameplay/src/RadioButton.h

@@ -44,14 +44,14 @@ public:
      * @param width The width to draw the radio button icon.
      * @param height The height to draw the radio button icon.
      */
-    void setIconSize(float width, float height);
+    void setImageSize(float width, float height);
 
     /**
      * Get the size at which the radio button icon will be drawn.
      *
      * @return The size of the radio button icon.
      */
-    const Vector2& getIconSize() const;
+    const Vector2& getImageSize() const;
 
     /**
      * Add a listener to be notified of specific events affecting
@@ -83,14 +83,14 @@ protected:
 
     void update(const Rectangle& clip);
 
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     // Clear the _selected flag of all radio buttons in the given group.
     static void clearSelected(const std::string& groupId);
 
     std::string _groupId;
     bool _selected;
-    Vector2 _iconSize;
+    Vector2 _imageSize;
 
 private:
     RadioButton(const RadioButton& copy);

+ 14 - 14
gameplay/src/Rectangle.cpp

@@ -9,12 +9,12 @@ Rectangle::Rectangle()
 {
 }
 
-Rectangle::Rectangle(float width, float height) :
+Rectangle::Rectangle(unsigned int width, unsigned int height) :
     x(0), y(0), width(width), height(height)
 {
 }
 
-Rectangle::Rectangle(float x, float y, float width, float height) :
+Rectangle::Rectangle(float x, float y, unsigned int width, unsigned int height) :
     x(x), y(y), width(width), height(height)
 {
 }
@@ -44,18 +44,18 @@ void Rectangle::set(const Rectangle& r)
     set(r.x, r.y, r.width, r.height);
 }
 
-void Rectangle::set(float x, float y)
+void Rectangle::set(float x, float y, unsigned int width, unsigned int height)
 {
     this->x = x;
     this->y = y;
+    this->width = width;
+    this->height = height;
 }
 
-void Rectangle::set(float x, float y, float width, float height)
+void Rectangle::setPosition(float x, float y)
 {
     this->x = x;
     this->y = y;
-    this->width = width;
-    this->height = height;
 }
 
 float Rectangle::left() const
@@ -70,22 +70,22 @@ float Rectangle::top() const
 
 float Rectangle::right() const
 {
-    return x + width;
+    return x + (float)width;
 }
 
 float Rectangle::bottom() const
 {
-    return y + height;
+    return y + (float)height;
 }
 
 bool Rectangle::contains(float x, float y) const
 {
-    return (x >= x && x <= (x + width) && y >= y && y <= (y + height));
+    return (x >= x && x <= (x + (float)width) && y >= y && y <= (y + (float)height));
 }
 
-bool Rectangle::contains(float x, float y, float width, float height) const
+bool Rectangle::contains(float x, float y, unsigned int width, unsigned int height) const
 {
-    return (contains(x, y) && contains(x + width, y + height));
+    return (contains(x, y) && contains(x + (float)width, y + (float)height));
 }
 
 bool Rectangle::contains(const Rectangle& r) const
@@ -93,12 +93,12 @@ bool Rectangle::contains(const Rectangle& r) const
     return contains(r.x, r.y, r.width, r.height);
 }
 
-bool Rectangle::intersects(float x, float y, float width, float height) const
+bool Rectangle::intersects(float x, float y, unsigned int width, unsigned int height) const
 {
     const float left   = max(this->x, x);
     const float top    = max(this->y, y);
-    const float right  = min(x + width, x + width);
-    const float bottom = min(y + height, y + height);
+    const float right  = min(x + (float)width, x + (float)width);
+    const float bottom = min(y + (float)height, y + (float)height);
 
     return (right > left && bottom > top);
 }

+ 14 - 14
gameplay/src/Rectangle.h

@@ -25,12 +25,12 @@ public:
     /**
      * Specifies the width of the rectangle.
      */
-    float width;
+    unsigned int width;
 
     /**
      * Specifies the height of the rectangle.
      */
-    float height;
+    unsigned int height;
 
     /**
      * Constructs a new rectangle initialized to all zeros.
@@ -43,7 +43,7 @@ public:
      * @param width The width of the rectangle.
      * @param height The height of the rectangle.
      */
-    Rectangle(float width, float height);
+    Rectangle(unsigned int width, unsigned int height);
 
     /**
      * Constructs a new rectangle with the specified x, y, width and height.
@@ -53,7 +53,7 @@ public:
      * @param width The width of the rectangle.
      * @param height The height of the rectangle.
      */
-    Rectangle(float x, float y, float width, float height);
+    Rectangle(float x, float y, unsigned int width, unsigned int height);
 
     /**
      * Constructs a new rectangle that is a copy of the specified rectangle.
@@ -89,22 +89,22 @@ public:
      * @param width The width of the rectangle.
      * @param height The height of the rectangle.
      */
-    void set(float x, float y, float width, float height);
+    void set(float x, float y, unsigned int width, unsigned int height);
 
     /**
-     * Sets the x-coordinate and y-coordinate values of this rectangle to the specified values.
+     * Sets the values of this rectangle to those in the specified rectangle.
      *
-     * @param x The x-coordinate of the rectangle.
-     * @param y The y-coordinate of the rectangle.
+     * @param r The rectangle to copy.
      */
-    void set(float x, float y);
+    void set(const Rectangle& r);
 
     /**
-     * Sets the values of this rectangle to those in the specified rectangle.
+     * Sets the x-coordinate and y-coordinate values of this rectangle to the specified values.
      *
-     * @param r The rectangle to copy.
+     * @param x The x-coordinate of the rectangle.
+     * @param y The y-coordinate of the rectangle.
      */
-    void set(const Rectangle& r);
+    void setPosition(float x, float y);
 
     /**
      * Returns the x-coordinate of the left side of the rectangle.
@@ -155,7 +155,7 @@ public:
      * @return true if the rectangle contains the specified rectangle, false
      * otherwise.
      */
-    bool contains(float x, float y, float width, float height) const;
+    bool contains(float x, float y, unsigned int width, unsigned int height) const;
 
     /**
      * Determines whether this rectangle contains a specified rectangle.
@@ -177,7 +177,7 @@ public:
      * 
      * @return true if the specified Rectangle intersects with this one, false otherwise.
      */
-    bool intersects(float x, float y, float width, float height) const;
+    bool intersects(float x, float y, unsigned int width, unsigned int height) const;
 
     /**
      * Determines whether a specified rectangle intersects with this rectangle.

+ 8 - 0
gameplay/src/Ref.cpp

@@ -18,6 +18,14 @@ Ref::Ref() :
 #endif
 }
 
+Ref::Ref(const Ref& copy) :
+    _refCount(1)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+    __record = trackRef(this);
+#endif
+}
+
 Ref::~Ref()
 {
 }

+ 2 - 0
gameplay/src/Ref.h

@@ -50,6 +50,8 @@ protected:
      */
     Ref();
 
+    Ref(const Ref& copy);
+
     /**
      * Destructor.
      */

+ 2 - 1
gameplay/src/RenderState.cpp

@@ -3,6 +3,7 @@
 #include "Node.h"
 #include "Pass.h"
 #include "Technique.h"
+#include "Node.h"
 
 // Render state override bits
 #define RS_BLEND 1
@@ -307,7 +308,7 @@ RenderState* RenderState::getTopmost(RenderState* below)
     return NULL;
 }
 
-void RenderState::cloneInto(RenderState* renderState, CloneContext& context) const
+void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context) const
 {
     for (std::map<std::string, AutoBinding>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
     {

+ 2 - 2
gameplay/src/RenderState.h

@@ -2,13 +2,13 @@
 #define RENDERSTATE_H_
 
 #include "Ref.h"
-#include "CloneContext.h"
 
 namespace gameplay
 {
 
 class MaterialParameter;
 class Node;
+class NodeCloneContext;
 class Pass;
 
 class RenderState : public Ref
@@ -338,7 +338,7 @@ protected:
      * @param renderState The RenderState to copy the data to.
      * @param context The clone context.
      */
-    void cloneInto(RenderState* renderState, CloneContext& context) const;
+    void cloneInto(RenderState* renderState, NodeCloneContext& context) const;
 
 private:
 

+ 0 - 10
gameplay/src/Scene.cpp

@@ -270,16 +270,6 @@ void Scene::bindAudioListenerToCamera(bool bind)
     }
 }
 
-const Viewport& Scene::getViewport() const
-{
-    return _viewport;
-}
-
-void Scene::setViewport(const Viewport& viewport)
-{
-    _viewport = viewport;
-}
-
 const Vector3& Scene::getAmbientColor() const
 {
     return _ambientColor;

+ 0 - 15
gameplay/src/Scene.h

@@ -142,20 +142,6 @@ public:
      */
     void bindAudioListenerToCamera(bool bind);
 
-    /**
-     * Gets the viewport for the scene.
-     *
-     * @return The scene's viewport.
-     */
-    const Viewport& getViewport() const;
-
-    /**
-     * Sets the scene's viewport.
-     *
-     * @param viewport The viewport to be set for this scene.
-     */
-    void setViewport(const Viewport& viewport);
-
     /**
      * Returns the ambient color of the scene. Black is the default color.
      * 
@@ -223,7 +209,6 @@ private:
 
     std::string _id;
     Camera* _activeCamera;
-    Viewport _viewport;
     Node* _firstNode;
     Node* _lastNode;
     unsigned int _nodeCount;

+ 58 - 28
gameplay/src/SceneLoader.cpp

@@ -51,9 +51,9 @@ Scene* SceneLoader::load(const char* filePath)
 
     // First apply the node url properties. Following that,
     // apply the normal node properties and create the animations.
-    // We apply rigid body properties after all other node properties
+    // We apply physics properties after all other node properties
     // so that the transform (SRT) properties get applied before
-    // processing rigid bodies.
+    // processing physics collision objects.
     applyNodeUrls(scene);
     applyNodeProperties(scene, sceneProperties, 
         SceneNodeProperty::AUDIO | 
@@ -63,7 +63,7 @@ Scene* SceneLoader::load(const char* filePath)
         SceneNodeProperty::SCALE |
         SceneNodeProperty::TRANSLATE | 
         SceneNodeProperty::TRANSPARENT);
-    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::RIGIDBODY);
+    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::CHARACTER | SceneNodeProperty::GHOST | SceneNodeProperty::RIGIDBODY);
     createAnimations(scene);
 
     // Find the physics properties object.
@@ -195,6 +195,8 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
     if (snp._type == SceneNodeProperty::AUDIO ||
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::PARTICLE ||
+        snp._type == SceneNodeProperty::CHARACTER ||
+        snp._type == SceneNodeProperty::GHOST ||
         snp._type == SceneNodeProperty::RIGIDBODY)
     {
         // Check to make sure the referenced properties object was loaded properly.
@@ -248,6 +250,8 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             SAFE_RELEASE(particleEmitter);
             break;
         }
+        case SceneNodeProperty::CHARACTER:
+        case SceneNodeProperty::GHOST:
         case SceneNodeProperty::RIGIDBODY:
         {
             // Check to make sure the referenced properties object was loaded properly.
@@ -275,38 +279,56 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 p = p->getNextNamespace();
             }
 
-            // If the scene file specifies a rigid body model, use it for creating the rigid body.
-            Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
-            const char* name = NULL;
-            if (np && (name = np->getString("rigidbodymodel")))
+            // Check to make sure the type of the namespace used to load the physics collision object is correct.
+            if (snp._type == SceneNodeProperty::CHARACTER && strcmp(p->getNamespace(), "character") != 0)
             {
-                Node* modelNode = node->getScene()->findNode(name);
-                if (!modelNode)
-                    WARN_VARG("Node '%s' does not exist; attempting to use its model for rigid body creation.", name);
-                else
+                WARN_VARG("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+            }
+            else if (snp._type == SceneNodeProperty::GHOST && strcmp(p->getNamespace(), "ghost") != 0)
+            {
+                WARN_VARG("Attempting to set a 'ghost' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+            }
+            else if (snp._type == SceneNodeProperty::RIGIDBODY && strcmp(p->getNamespace(), "rigidbody") != 0)
+            {
+                WARN_VARG("Attempting to set a 'rigidbody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+            }
+            else
+            {
+                // If the scene file specifies a rigid body model, use it for creating the collision object.
+                Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
+                const char* name = NULL;
+                if (np && (name = np->getString("rigidbodymodel")))
                 {
-                    if (!modelNode->getModel())
-                        WARN_VARG("Node '%s' does not have a model; attempting to use its model for rigid body creation.", name);
+                    Node* modelNode = node->getScene()->findNode(name);
+                    if (!modelNode)
+                        WARN_VARG("Node '%s' does not exist; attempting to use its model for collision object creation.", name);
                     else
                     {
-                        // Temporarily set rigidbody model on model to it's used during rigid body creation.
-                        Model* model = node->getModel();
-						model->addRef(); // up ref count to prevent node from releasing the model when we swap it
-                        node->setModel(modelNode->getModel());
-
-						// Create collision object with new rigidbodymodel set.
-						node->setCollisionObject(p);
-
-						// Restore original model
-                        node->setModel(model);
-						model->release(); // decrement temporarily added reference
+                        if (!modelNode->getModel())
+                            WARN_VARG("Node '%s' does not have a model; attempting to use its model for collision object creation.", name);
+                        else
+                        {
+                            // Temporarily set rigidbody model on model so it's used during collision object creation.
+                            Model* model = node->getModel();
+                        
+                            // Up ref count to prevent node from releasing the model when we swap it.
+						    model->addRef(); 
+                        
+						    // Create collision object with new rigidbodymodel set.
+                            node->setModel(modelNode->getModel());
+						    node->setCollisionObject(p);
+
+						    // Restore original model.
+                            node->setModel(model);
+						
+                            // Decrement temporarily added reference.
+                            model->release();
+                        }
                     }
                 }
+                else
+				    node->setCollisionObject(p);
             }
-            else if (!node->getModel())
-                WARN_VARG("Attempting to set a rigid body on node '%s', which has no model.", sceneNode._nodeID);
-            else
-				node->setCollisionObject(p);
             break;
         }
         default:
@@ -528,6 +550,14 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, ns->getString());
                 }
+                else if (strcmp(name, "character") == 0)
+                {
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::CHARACTER, ns->getString());
+                }
+                else if (strcmp(name, "ghost") == 0)
+                {
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::GHOST, ns->getString());
+                }
                 else if (strcmp(name, "rigidbody") == 0)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::RIGIDBODY, ns->getString());

+ 8 - 6
gameplay/src/SceneLoader.h

@@ -49,12 +49,14 @@ private:
             AUDIO = 1,
             MATERIAL = 2,
             PARTICLE = 4,
-            RIGIDBODY = 8,
-            TRANSLATE = 16,
-            ROTATE = 32,
-            SCALE = 64,
-            URL = 128,
-            TRANSPARENT = 256
+            CHARACTER = 8,
+            GHOST = 16,
+            RIGIDBODY = 32,
+            TRANSLATE = 64,
+            ROTATE = 128,
+            SCALE = 256,
+            URL = 512,
+            TRANSPARENT = 1024
         };
 
         SceneNodeProperty(Type type, std::string file, std::string id, int index) : _type(type), _file(file), _id(id), _index(index) { }

+ 49 - 58
gameplay/src/Slider.cpp

@@ -95,22 +95,14 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             y > 0 && y <= _bounds.height)
         {
             // Horizontal case.
-            Theme::Border border;
-            Theme::ContainerRegion* containerRegion = _style->getOverlay(getOverlayType())->getContainerRegion();
-            if (containerRegion)
-            {
-                border = containerRegion->getBorder();
-            }
-            Theme::Padding padding = _style->getPadding();
-
-            const Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-            const Theme::SliderIcon* icon = overlay->getSliderIcon();
-
-            const Vector2 minCapSize = icon->getMinCapSize();
-            const Vector2 maxCapSize = icon->getMaxCapSize();
+            const Theme::Border& border = getBorder(_state);
+            const Theme::Padding& padding = getPadding();
+            const Rectangle& minCapRegion = getImageRegion("minCap", _state);
+            const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
 
-            float markerPosition = ((float)x - maxCapSize.x - border.left - padding.left) /
-                                    (_bounds.width - border.left - border.right - padding.left - padding.right - minCapSize.x - maxCapSize.x);
+            float markerPosition = ((float)x - maxCapRegion.width - border.left - padding.left) /
+                (_bounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
+            
             if (markerPosition > 1.0f)
             {
                 markerPosition = 1.0f;
@@ -143,56 +135,55 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return Control::touchEvent(evt, x, y, contactIndex);
 }
 
-void Slider::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // TODO: Vertical slider.
 
     // The slider is drawn in the center of the control (perpendicular to orientation).
     // The track is stretched according to orientation.
     // Caps and marker are not stretched.
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::SliderIcon* icon = overlay->getSliderIcon();
-    if (icon)
-    {
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-        if (containerRegion)
-        {
-            border = containerRegion->getBorder();
-        }
-        Theme::Padding padding = _style->getPadding();
-
-        const Vector2 minCapSize = icon->getMinCapSize();
-        const Vector2 maxCapSize = icon->getMaxCapSize();
-        const Vector2 markerSize = icon->getMarkerSize();
-        const Vector2 trackSize = icon->getTrackSize();
-
-        const Theme::UVs minCap = icon->getMinCapUVs();
-        const Theme::UVs maxCap = icon->getMaxCapUVs();
-        const Theme::UVs marker = icon->getMarkerUVs();
-        const Theme::UVs track = icon->getTrackUVs();
-
-        const Vector4 color = icon->getColor();
-
-        // Draw order: track, caps, marker.
-        float midY = clip.y + _bounds.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f;
-        Vector2 pos(clip.x + _bounds.x + border.left + padding.left, midY - trackSize.y / 2.0f);
-        spriteBatch->draw(pos.x, pos.y, _bounds.width, trackSize.y, track.u1, track.v1, track.u2, track.v2, color);
-
-        pos.y = midY - minCapSize.y * 0.5f;
-        pos.x -= minCapSize.x * 0.5f;
-        spriteBatch->draw(pos.x, pos.y, minCapSize.x, minCapSize.y, minCap.u1, minCap.v1, minCap.u2, minCap.v2, color);
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
+
+    const Rectangle& minCapRegion = getImageRegion("minCap", _state);
+    const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
+    const Rectangle& markerRegion = getImageRegion("marker", _state);
+    const Rectangle& trackRegion = getImageRegion("track", _state);
+
+    const Theme::UVs minCap = getImageUVs("minCap", _state);
+    const Theme::UVs maxCap = getImageUVs("maxCap", _state);
+    const Theme::UVs marker = getImageUVs("marker", _state);
+    const Theme::UVs track = getImageUVs("track", _state);
+
+    Vector4 minCapColor = getImageColor("minCap", _state);
+    Vector4 maxCapColor = getImageColor("maxCap", _state);
+    Vector4 markerColor = getImageColor("marker", _state);
+    Vector4 trackColor = getImageColor("track", _state);
+
+    float opacity = getOpacity(_state);
+    minCapColor.w *= opacity;
+    maxCapColor.w *= opacity;
+    markerColor.w *= opacity;
+    trackColor.w *= opacity;
+
+    // Draw order: track, caps, marker.
+    float midY = clip.y + _bounds.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f;
+    Vector2 pos(clip.x + _bounds.x + border.left + padding.left, midY - trackRegion.height / 2.0f);
+    spriteBatch->draw(pos.x, pos.y, _bounds.width, trackRegion.height, track.u1, track.v1, track.u2, track.v2, trackColor);
+
+    pos.y = midY - minCapRegion.height * 0.5f;
+    pos.x -= minCapRegion.width * 0.5f;
+    spriteBatch->draw(pos.x, pos.y, minCapRegion.width, minCapRegion.height, minCap.u1, minCap.v1, minCap.u2, minCap.v2, minCapColor);
         
-        pos.x = clip.x + _bounds.x + _bounds.width - border.right - padding.right - maxCapSize.x * 0.5f;
-        spriteBatch->draw(pos.x, pos.y, maxCapSize.x, maxCapSize.y, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, color);
-
-        // Percent across.
-        float markerPosition = _value / (_max - _min);
-        markerPosition *= _bounds.width - border.left - padding.left - border.right - padding.right - minCapSize.x * 0.5f - maxCapSize.x * 0.5f - markerSize.x;
-        pos.x = clip.x + _bounds.x + border.left + padding.left + minCapSize.x * 0.5f + markerPosition;
-        pos.y = midY - markerSize.y / 2.0f;
-        spriteBatch->draw(pos.x, pos.y, markerSize.x, markerSize.y, marker.u1, marker.v1, marker.u2, marker.v2, color);
-    }
+    pos.x = clip.x + _bounds.x + _bounds.width - border.right - padding.right - maxCapRegion.width * 0.5f;
+    spriteBatch->draw(pos.x, pos.y, maxCapRegion.width, maxCapRegion.height, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, maxCapColor);
+
+    // Percent across.
+    float markerPosition = _value / (_max - _min);
+    markerPosition *= _bounds.width - border.left - padding.left - border.right - padding.right - minCapRegion.width * 0.5f - maxCapRegion.width * 0.5f - markerRegion.width;
+    pos.x = clip.x + _bounds.x + border.left + padding.left + minCapRegion.width * 0.5f + markerPosition;
+    pos.y = midY - markerRegion.height / 2.0f;
+    spriteBatch->draw(pos.x, pos.y, markerRegion.width, markerRegion.height, marker.u1, marker.v1, marker.u2, marker.v2, markerColor);
 }
 
 }

+ 1 - 1
gameplay/src/Slider.h

@@ -100,7 +100,7 @@ protected:
 
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     float _min;
     float _max;

+ 2 - 1
gameplay/src/Technique.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "Technique.h"
 #include "Material.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -53,7 +54,7 @@ Pass* Technique::getPass(const char* id) const
     return NULL;
 }
 
-Technique* Technique::clone(Material* material, CloneContext &context) const
+Technique* Technique::clone(Material* material, NodeCloneContext &context) const
 {
     Technique* technique = new Technique(getId(), material);
     technique->_material = material;

+ 2 - 1
gameplay/src/Technique.h

@@ -7,6 +7,7 @@ namespace gameplay
 {
 
 class Material;
+class NodeCloneContext;
 
 /**
  * Defines a technique for an object to be rendered.
@@ -63,7 +64,7 @@ private:
      */
     Technique& operator=(const Technique&);
 
-    Technique* clone(Material* material, CloneContext &context) const;
+    Technique* clone(Material* material, NodeCloneContext &context) const;
 
     std::string _id;
     Material* _material;

+ 9 - 44
gameplay/src/TextBox.cpp

@@ -31,14 +31,8 @@ int TextBox::getLastKeypress()
 
 void TextBox::setCursorLocation(int x, int y)
 {
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-    Theme::Border border;
-    if (containerRegion)
-    {
-        border = containerRegion->getBorder();
-    }
-    Theme::Padding padding = _style->getPadding();
+    Theme::Border border = getBorder(_state);
+    Theme::Padding padding = getPadding();
 
     _cursorLocation.set(x - border.left - padding.left + _clip.x,
                        y - border.top - padding.top + _clip.y);
@@ -251,26 +245,6 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 
 void TextBox::update(const Rectangle& clip)
 {
-    /*
-    Vector2 pos(clip.x + _position.x, clip.y + _position.y);
-    Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-    Theme::Border border;
-    Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-    if (containerRegion)
-    {
-        border = containerRegion->getBorder();
-    }
-    Theme::Padding padding = _style->getPadding();
-
-    // Set up the text viewport.
-    Font* font = overlay->getFont();
-    _clip.set(pos.x + border.left + padding.left,
-                  pos.y + border.top + padding.top,
-                  _size.x - border.left - padding.left - border.right - padding.right,
-                  _size.y - border.top - padding.top - border.bottom - padding.bottom - overlay->getFontSize());
-
-                  */
-
     Control::update(clip);
 
     // Get index into string and cursor location from the last recorded touch location.
@@ -283,28 +257,19 @@ void TextBox::update(const Rectangle& clip)
     }
 }
 
-void TextBox::drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip)
+void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     if (_state == FOCUS)
     {
         // Draw the cursor at its current location.
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Theme::Cursor* cursor = overlay->getTextCursor();
-        if (cursor)
+        const Rectangle& region = getImageRegion("textCaret", _state);
+        if (!region.isEmpty())
         {
-            Theme::Border border;
-            Theme::ContainerRegion* containerRegion = overlay->getContainerRegion();
-            if (containerRegion)
-            {
-                border = containerRegion->getBorder();
-            }
-            const Theme::Padding padding = _style->getPadding();
-            const Vector2 size = cursor->getSize();
-            const Vector4 color = cursor->getColor();
-            const Theme::UVs uvs = cursor->getUVs();
-            unsigned int fontSize = overlay->getFontSize();
+            const Vector4& color = getImageColor("textCaret", _state);
+            const Theme::UVs uvs = getImageUVs("textCaret", _state);
+            unsigned int fontSize = getFontSize(_state);
 
-            spriteBatch->draw(_cursorLocation.x - (size.x / 2.0f), _cursorLocation.y, size.x, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+            spriteBatch->draw(_cursorLocation.x - (region.width / 2.0f), _cursorLocation.y, region.width, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
         }
     }
 

+ 1 - 1
gameplay/src/TextBox.h

@@ -60,7 +60,7 @@ protected:
     void update(const Rectangle& clip);
 
     // Draw the cursor.
-    void drawSprites(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     Vector2 _cursorLocation;
     unsigned int textIndex;

+ 505 - 421
gameplay/src/Theme.cpp

@@ -1,7 +1,3 @@
-/*
- * Theme.cpp
- */
-
 #include "Base.h"
 #include "Theme.h"
 
@@ -26,35 +22,29 @@ namespace gameplay
             SAFE_DELETE(style);
         }
 
-        for (unsigned int i = 0, count = _cursors.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _images.size(); i < count; ++i)
         {
-            Cursor* cursor = _cursors[i];
-            SAFE_RELEASE(cursor);
+            Image* image = _images[i];
+            SAFE_RELEASE(image);
         }
 
-        for (unsigned int i = 0, count = _icons.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _imageLists.size(); i < count; ++i)
         {
-            Icon* icon = _icons[i];
-            SAFE_RELEASE(icon);
+            ImageList* imageList = _imageLists[i];
+            SAFE_RELEASE(imageList);
         }
 
-        for (unsigned int i = 0, count = _sliders.size(); i < count; ++i)
+        for (unsigned int i = 0, count = _skins.size(); i < count; ++i)
         {
-            SliderIcon* slider = _sliders[i];
-            SAFE_RELEASE(slider);
-        }
-
-        for (unsigned int i = 0, count = _containers.size(); i < count; ++i)
-        {
-            ContainerRegion* container = _containers[i];
-            SAFE_RELEASE(container);
+            Skin* skin = _skins[i];
+            SAFE_RELEASE(skin);
         }
 
         SAFE_DELETE(_spriteBatch);
         SAFE_RELEASE(_texture);
 
         // Remove ourself from the theme cache.
-        std::vector<Theme*>::iterator itr = find(__themeCache.begin(), __themeCache.end(), this);
+        std::vector<Theme*>::iterator itr = std::find(__themeCache.begin(), __themeCache.end(), this);
         if (itr != __themeCache.end())
         {
             __themeCache.erase(itr);
@@ -104,80 +94,24 @@ namespace gameplay
         theme->_texture = Texture::create(textureFile, false);
         theme->_spriteBatch = SpriteBatch::create(theme->_texture);
 
+        float tw = 1.0f / theme->_texture->getWidth();
+        float th = 1.0f / theme->_texture->getHeight();
+
         Properties* space = themeProperties->getNextNamespace();
         while (space != NULL)
         {
             // First load all cursors, checkboxes etc. that can be referred to by styles.
             const char* spacename = space->getNamespace();
-            if (strcmp(spacename, "cursor") == 0)
-            {
-                Vector4 regionVector;                
-                space->getVector4("region", &regionVector);
-                const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
-
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                Theme::Cursor* c = Theme::Cursor::create(space->getId(), *theme->_texture, region, color);
-                theme->_cursors.push_back(c);
-            }
-            else if (strcmp(spacename, "icon") == 0)
-            {
-                Vector2 offVec;
-                Vector2 onVec;
-                Vector2 size;
-                space->getVector2("offPosition", &offVec);
-                space->getVector2("onPosition", &onVec);
-                space->getVector2("size", &size);
-                
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                Icon* icon = Icon::create(space->getId(), *theme->_texture, size, offVec, onVec, color);
-                theme->_icons.push_back(icon);
-            }
-            else if (strcmp(spacename, "slider") == 0)
+            
+            if (strcmp(spacename, "image") == 0)
             {
-                Vector4 minCapRegion;
-                Vector4 maxCapRegion;
-                Vector4 trackRegion;
-                Vector4 markerRegion;
-                space->getVector4("minCapRegion", &minCapRegion);
-                space->getVector4("maxCapRegion", &maxCapRegion);
-                space->getVector4("trackRegion", &trackRegion);
-                space->getVector4("markerRegion", &markerRegion);
-                
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                SliderIcon* sliderIcon = SliderIcon::create(space->getId(), *theme->_texture, minCapRegion, maxCapRegion, markerRegion, trackRegion, color);
-                theme->_sliders.push_back(sliderIcon);
+                theme->_images.push_back(Image::create(tw, th, space, Vector4::one()));
             }
-            else if (strcmp(spacename, "cursor") == 0)
+            else if (strcmp(spacename, "imageList") == 0)
             {
-                Vector4 regionVector;
-                space->getVector4("region", &regionVector);
-                const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
-
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
-                }
-
-                Cursor* cursor = Cursor::create(space->getId(), *theme->_texture, region, color);
-                theme->_cursors.push_back(cursor);
+                theme->_imageLists.push_back(ImageList::create(tw, th, space));
             }
-            else if (strcmp(spacename, "container") == 0)
+            else if (strcmp(spacename, "skin") == 0)
             {
                 Theme::Border border;
                 Properties* innerSpace = space->getNextNamespace();
@@ -203,8 +137,8 @@ namespace gameplay
                     space->getColor("color", &color);
                 }
 
-                ContainerRegion* container = ContainerRegion::create(space->getId(), *theme->_texture, region, border, color);
-                theme->_containers.push_back(container);
+                Skin* skin = Skin::create(space->getId(), tw, th, region, border, color);
+                theme->_skins.push_back(skin);
             }
 
             space = themeProperties->getNextNamespace();
@@ -231,7 +165,7 @@ namespace gameplay
                 while (innerSpace != NULL)
                 {
                     const char* innerSpacename = innerSpace->getNamespace();
-                    if (strcmp(innerSpacename, "normal") == 0)
+                    if (strcmp(innerSpacename, "stateNormal") == 0)
                     {
                         Vector4 textColor(0, 0, 0, 1);
                         if (innerSpace->exists("textColor"))
@@ -254,26 +188,27 @@ namespace gameplay
                         }
                         bool rightToLeft = innerSpace->getBool("rightToLeft");
 
-                        Icon* checkBoxIcon = NULL;
-                        Icon* radioButtonIcon = NULL;
-                        SliderIcon* sliderIcon = NULL;
-                        Cursor* textCursor = NULL;
-                        Cursor* mouseCursor = NULL;
-                        ContainerRegion* containerRegion = NULL;
-                        theme->lookUpSprites(innerSpace, &checkBoxIcon, &radioButtonIcon, &sliderIcon, &textCursor, &mouseCursor, &containerRegion);
+                        float opacity = 1.0f;
+                        if (innerSpace->exists("opacity"))
+                        {
+                            opacity = innerSpace->getFloat("opacity");
+                        }
+
+                        ImageList* imageList = NULL;
+                        Image* cursor = NULL;
+                        Skin* skin = NULL;
+                        theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
 
                         normal = Theme::Style::Overlay::create();
-                        normal->setContainerRegion(containerRegion);
-                        normal->setTextCursor(textCursor);
-                        normal->setMouseCursor(mouseCursor);
-                        normal->setCheckBoxIcon(checkBoxIcon);
-                        normal->setRadioButtonIcon(radioButtonIcon);
-                        normal->setSliderIcon(sliderIcon);
+                        normal->setSkin(skin);
+                        normal->setCursor(cursor);
+                        normal->setImageList(imageList);
                         normal->setTextColor(textColor);
                         normal->setFont(font);
                         normal->setFontSize(fontSize);
                         normal->setTextAlignment(alignment);
                         normal->setTextRightToLeft(rightToLeft);
+                        normal->setOpacity(opacity);
 
                         theme->_fonts.insert(font);
 
@@ -308,7 +243,7 @@ namespace gameplay
                         padding.left = innerSpace->getFloat("left");
                         padding.right = innerSpace->getFloat("right");
                     }
-                    else if (strcmp(innerSpacename, "normal") != 0)
+                    else if (strcmp(innerSpacename, "stateNormal") != 0)
                     {
                         // Either OVERLAY_FOCUS or OVERLAY_ACTIVE.
                         // If a property isn't specified, it inherits from OVERLAY_NORMAL.
@@ -360,94 +295,78 @@ namespace gameplay
                             rightToLeft = normal->getTextRightToLeft();
                         }
 
-
-                        Icon* checkBoxIcon = NULL;
-                        Icon* radioButtonIcon = NULL;
-                        SliderIcon* sliderIcon = NULL;
-                        Cursor* textCursor = NULL;
-                        Cursor* mouseCursor = NULL;
-                        ContainerRegion* containerRegion = NULL;
-                        theme->lookUpSprites(innerSpace, &checkBoxIcon, &radioButtonIcon, &sliderIcon, &textCursor, &mouseCursor, &containerRegion);
-
-                        if (!checkBoxIcon)
+                        float opacity;
+                        if (innerSpace->exists("opacity"))
                         {
-                            checkBoxIcon = normal->getCheckBoxIcon();
+                            opacity = innerSpace->getFloat("opacity");
                         }
-                        
-                        if (!radioButtonIcon)
-                        {
-                            radioButtonIcon = normal->getRadioButtonIcon();
-                        }
-                        
-                        if (!sliderIcon)
+                        else
                         {
-                            sliderIcon = normal->getSliderIcon();
+                            opacity = normal->getOpacity();
                         }
 
-                        if (!textCursor)
+                        ImageList* imageList = NULL;
+                        Image* cursor = NULL;
+                        Skin* skin = NULL;
+                        theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
+
+                        if (!imageList)
                         {
-                            textCursor = normal->getTextCursor();
+                            imageList = normal->getImageList();
                         }
 
-                        if (!mouseCursor)
+                        if (!cursor)
                         {
-                            mouseCursor = normal->getMouseCursor();
+                            cursor = normal->getCursor();
                         }
                         
-                        if (!containerRegion)
+                        if (!skin)
                         {
-                            containerRegion = normal->getContainerRegion();
+                            skin = normal->getSkin();
                         }
 
-                        if (strcmp(innerSpacename, "focus") == 0)
+                        if (strcmp(innerSpacename, "stateFocus") == 0)
                         {
                             focus = Theme::Style::Overlay::create();
-                            focus->setContainerRegion(containerRegion);
-                            focus->setCheckBoxIcon(checkBoxIcon);
-                            focus->setTextCursor(textCursor);
-                            focus->setMouseCursor(mouseCursor);
-                            focus->setCheckBoxIcon(checkBoxIcon);
-                            focus->setRadioButtonIcon(radioButtonIcon);
-                            focus->setSliderIcon(sliderIcon);
+                            focus->setSkin(skin);
+                            focus->setCursor(cursor);
+                            focus->setImageList(imageList);
                             focus->setTextColor(textColor);
                             focus->setFont(font);
                             focus->setFontSize(fontSize);
                             focus->setTextAlignment(alignment);
                             focus->setTextRightToLeft(rightToLeft);
+                            focus->setOpacity(opacity);
 
                             theme->_fonts.insert(font);
                         }
-                        else if (strcmp(innerSpacename, "active") == 0)
+                        else if (strcmp(innerSpacename, "stateActive") == 0)
                         {
                             active = Theme::Style::Overlay::create();
-                            active->setContainerRegion(containerRegion);
-                            active->setTextCursor(textCursor);
-                            active->setMouseCursor(mouseCursor);
-                            active->setCheckBoxIcon(checkBoxIcon);
-                            active->setRadioButtonIcon(radioButtonIcon);
-                            active->setSliderIcon(sliderIcon);
+                            active->setSkin(skin);
+                            active->setCursor(cursor);
+                            active->setImageList(imageList);
                             active->setTextColor(textColor);
                             active->setFont(font);
                             active->setFontSize(fontSize);
                             active->setTextAlignment(alignment);
                             active->setTextRightToLeft(rightToLeft);
+                            active->setOpacity(opacity);
 
                             theme->_fonts.insert(font);
                         }
-                        else if (strcmp(innerSpacename, "disabled") == 0)
+                        else if (strcmp(innerSpacename, "stateDisabled") == 0)
                         {
                             disabled = Theme::Style::Overlay::create();
-                            disabled->setContainerRegion(containerRegion);
-                            disabled->setTextCursor(textCursor);
-                            disabled->setMouseCursor(mouseCursor);
-                            disabled->setCheckBoxIcon(checkBoxIcon);
-                            disabled->setRadioButtonIcon(radioButtonIcon);
-                            disabled->setSliderIcon(sliderIcon);
+                            disabled->setSkin(skin);
+                            disabled->setCursor(cursor);
+                            disabled->setImageList(imageList);
                             disabled->setTextColor(textColor);
                             disabled->setFont(font);
                             disabled->setFontSize(fontSize);
                             disabled->setTextAlignment(alignment);
                             disabled->setTextRightToLeft(rightToLeft);
+                            disabled->setOpacity(opacity);
 
                             theme->_fonts.insert(font);
                         }
@@ -474,7 +393,7 @@ namespace gameplay
                     disabled->addRef();
                 }
 
-                Theme::Style* s = new Theme::Style(space->getId(), margin, padding, normal, focus, active, disabled);
+                Theme::Style* s = new Theme::Style(space->getId(), tw, th, margin, padding, normal, focus, active, disabled);
                 theme->_styles.push_back(s);
             }
 
@@ -519,223 +438,222 @@ namespace gameplay
         return _spriteBatch;
     }
 
-    /***************
-     * Theme::Icon *
-     ***************/
-    Theme::Icon* Theme::Icon::create(const char* id, const Texture& texture, const Vector2& size,
-                                     const Vector2& offPosition, const Vector2& onPosition, const Vector4& color)
+    /**************
+     * Theme::UVs *
+     **************/
+    Theme::UVs::UVs()
+        : u1(0), v1(0), u2(0), v2(0)
     {
-        Icon* icon = new Icon(texture, size, offPosition, onPosition, color);
-
-        if (id)
-        {
-            icon->_id = id;
-        }
-
-        return icon;
     }
 
-    Theme::Icon::Icon(const Texture& texture, const Vector2& size,
-                      const Vector2& offPosition, const Vector2& onPosition, const Vector4& color)
-                      : _size(size), _color(color)
+    Theme::UVs::UVs(float u1, float v1, float u2, float v2)
+        : u1(u1), v1(v1), u2(u2), v2(v2)
     {
-        generateUVs(texture, offPosition.x, offPosition.y, size.x, size.y, &_off);
-        generateUVs(texture, onPosition.x, onPosition.y, size.x, size.y, &_on);
     }
 
-    Theme::Icon::~Icon()
+    const Theme::UVs& Theme::UVs::empty()
     {
+        static UVs empty(0, 0, 0, 0);
+        return empty;
     }
 
-    const char* Theme::Icon::getId() const
-    {
-        return _id.c_str();
-    }
-
-    const Vector2& Theme::Icon::getSize() const
+    /**********************
+     * Theme::SideRegions *
+     **********************/
+    const Theme::SideRegions& Theme::SideRegions::empty()
     {
-        return _size;
+        static SideRegions empty;
+        return empty;
     }
 
-    const Vector4& Theme::Icon::getColor() const
+    /****************
+     * Theme::Image *
+     ****************/
+    Theme::Image::Image(float tw, float th, const Rectangle& region, const Vector4& color)
+        : _region(region), _color(color)
     {
-        return _color;
+        generateUVs(tw, th, region.x, region.y, region.width, region.height, &_uvs);
     }
 
-    const Theme::UVs& Theme::Icon::getOffUVs() const
+    Theme::Image::~Image()
     {
-        return _off;
     }
 
-    const Theme::UVs& Theme::Icon::getOnUVs() const
+    Theme::Image* Theme::Image::create(float tw, float th, Properties* properties, const Vector4& defaultColor)
     {
-        return _on;
-    }
-
+        Vector4 regionVector;                
+        properties->getVector4("region", &regionVector);
+        const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
 
-    /*********************
-     * Theme::SliderIcon *
-     *********************/
-    Theme::SliderIcon* Theme::SliderIcon::create(const char* id, const Texture& texture, const Vector4& minCapRegion,
-            const Vector4& maxCapRegion, const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color)
-    {
-        SliderIcon* sliderIcon = new SliderIcon(texture, minCapRegion, maxCapRegion, markerRegion, trackRegion, color);
+        Vector4 color;
+        if (properties->exists("color"))
+        {
+            properties->getColor("color", &color);
+        }
+        else
+        {
+            color.set(defaultColor);
+        }
 
+        Image* image = new Image(tw, th, region, color);
+        const char* id = properties->getId();
         if (id)
         {
-            sliderIcon->_id = id;
+            image->_id = id;
         }
 
-        return sliderIcon;
+        return image;
     }
 
-    Theme::SliderIcon::SliderIcon(const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
-                                  const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color)
-                                  : _color(color)
+    const char* Theme::Image::getId() const
     {
-        _minCapSize.set(minCapRegion.z, minCapRegion.w);
-        _maxCapSize.set(maxCapRegion.z, maxCapRegion.w);
-        _markerSize.set(markerRegion.z, markerRegion.w);
-        _trackSize.set(trackRegion.z, trackRegion.w);
-
-        generateUVs(texture, minCapRegion.x, minCapRegion.y, minCapRegion.z, minCapRegion.w, &_minCap);
-        generateUVs(texture, maxCapRegion.x, maxCapRegion.y, maxCapRegion.z, maxCapRegion.w, &_maxCap);
-        generateUVs(texture, markerRegion.x, markerRegion.y, markerRegion.z, markerRegion.w, &_marker);
-        generateUVs(texture, trackRegion.x, trackRegion.y, trackRegion.z, trackRegion.w, &_track);
+        return _id.c_str();
     }
 
-    Theme::SliderIcon::~SliderIcon()
+    const Theme::UVs& Theme::Image::getUVs() const
     {
+        return _uvs;
     }
 
-    const char* Theme::SliderIcon::getId() const
+    const Rectangle& Theme::Image::getRegion() const
     {
-        return _id.c_str();
+        return _region;
     }
 
-    const Theme::UVs& Theme::SliderIcon::getMinCapUVs() const
+    const Vector4& Theme::Image::getColor() const
     {
-        return _minCap;
+        return _color;
     }
 
-    const Theme::UVs& Theme::SliderIcon::getMaxCapUVs() const
+    /********************
+     * Theme::ImageList *
+     ********************/
+    Theme::ImageList::ImageList(const Vector4& color) : _color(color)
     {
-        return _maxCap;
-    }
-    
-    const Theme::UVs& Theme::SliderIcon::getMarkerUVs() const
-    {
-        return _marker;
-    }
-    
-    const Theme::UVs& Theme::SliderIcon::getTrackUVs() const
-    {
-        return _track;
     }
 
-    const Vector2& Theme::SliderIcon::getMinCapSize() const
+    Theme::ImageList::ImageList(const ImageList& copy)
     {
-        return _minCapSize;
-    }
+        _id = copy._id;
+        _color = copy._color;
 
-    const Vector2& Theme::SliderIcon::getMaxCapSize() const
-    {
-        return _maxCapSize;
+        std::vector<Image*>::const_iterator it;
+        for (it = copy._images.begin(); it != copy._images.end(); it++)
+        {
+            Image* image = *it;
+            _images.push_back(new Image(*image));
+        }
     }
 
-    const Vector2& Theme::SliderIcon::getMarkerSize() const
+    Theme::ImageList::~ImageList()
     {
-        return _markerSize;
+        std::vector<Image*>::const_iterator it;
+        for (it = _images.begin(); it != _images.end(); it++)
+        {
+            Image* image = *it;
+            SAFE_RELEASE(image);
+        }
     }
 
-    const Vector2& Theme::SliderIcon::getTrackSize() const
+    Theme::ImageList* Theme::ImageList::create(float tw, float th, Properties* properties)
     {
-        return _trackSize;
-    }
+        Vector4 color(1, 1, 1, 1);
+        if (properties->exists("color"))
+        {
+            properties->getColor("color", &color);
+        }
 
-    const Vector4& Theme::SliderIcon::getColor() const
-    {
-        return _color;
-    }
+        ImageList* imageList = new ImageList(color);
 
-    /*****************
-     * Theme::Cursor *
-     *****************/
-    Theme::Cursor* Theme::Cursor::create(const char* id, const Texture& texture, const Rectangle& region, const Vector4& color)
-    {
-        Cursor* cursor = new Cursor(texture, region, color);
-        
+        const char* id = properties->getId();
         if (id)
         {
-            cursor->_id = id;
+            imageList->_id = id;
+        }
+
+        Properties* space = properties->getNextNamespace();
+        while (space != NULL)
+        {
+            Image* image = Image::create(tw, th, space, color);
+            imageList->_images.push_back(image);
+            space = properties->getNextNamespace();
         }
 
-        return cursor;
+        return imageList;
     }
 
-    Theme::Cursor::Cursor(const Texture& texture, const Rectangle& region, const Vector4& color)
-        : _color(color)
+    const char* Theme::ImageList::getId() const
     {
-        _size.set(region.width, region.height);
-        generateUVs(texture, region.x, region.y, region.width, region.height, &_uvs);
+        return _id.c_str();
     }
 
-    Theme::Cursor::~Cursor()
+    Theme::Image* Theme::ImageList::getImage(const char* imageId) const
     {
+        std::vector<Image*>::const_iterator it;
+        for (it = _images.begin(); it != _images.end(); it++)
+        {
+            Image* image = *it;
+            if (strcmp(image->getId(), imageId) == 0)
+            {
+                return image;
+            }
+        }
+
+        return NULL;
     }
 
-    const char* Theme::Cursor::getId() const
+    /***************
+     * Theme::Skin *
+     ***************/
+    Theme::Skin* Theme::Skin::create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
     {
-        return _id.data();
+        Skin* skin = new Skin(tw, th, region, border, color);
+
+        if (id)
+        {
+            skin->_id = id;
+        }
+
+        return skin;
     }
 
-    const Theme::UVs& Theme::Cursor::getUVs() const
+    Theme::Skin::Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
+        : _border(border), _color(color), _region(region)
     {
-        return _uvs;
+        setRegion(region, tw, th);
     }
 
-    const Vector2& Theme::Cursor::getSize() const
+    Theme::Skin::~Skin()
     {
-        return _size;
     }
 
-    const Vector4& Theme::Cursor::getColor() const
+    const char* Theme::Skin::getId() const
     {
-        return _color;
+        return _id.c_str();
     }
 
-    /**************************
-     * Theme::ContainerRegion *
-     **************************/
-    Theme::ContainerRegion* Theme::ContainerRegion::create(const char* id, const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color)
+    const Theme::Border& Theme::Skin::getBorder() const
     {
-        ContainerRegion* containerRegion = new ContainerRegion(texture, region, border, color);
-
-        if (id)
-        {
-            containerRegion->_id = id;
-        }
-
-        return containerRegion;
+        return _border;
     }
 
-    Theme::ContainerRegion::ContainerRegion(const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color)
-        : _border(border), _color(color)
+    const Rectangle& Theme::Skin::getRegion() const
     {
-        // Need to convert pixel coords to unit space by dividing by texture size.
-        float tw = 1.0f / (float)texture.getWidth();
-        float th = 1.0f / (float)texture.getHeight();
+        return _region;
+    }
 
+    void Theme::Skin::setRegion(const Rectangle& region, float tw, float th)
+    {
         // Can calculate all measurements in advance.
         float leftEdge = region.x * tw;
         float rightEdge = (region.x + region.width) * tw;
-        float leftBorder = (region.x + border.left) * tw;
-        float rightBorder = (region.x + region.width - border.right) * tw;
+        float leftBorder = (region.x + _border.left) * tw;
+        float rightBorder = (region.x + region.width - _border.right) * tw;
 
         float topEdge = 1.0f - (region.y * th);
         float bottomEdge = 1.0f - ((region.y + region.height) * th);
-        float topBorder = 1.0f - ((region.y + border.top) * th);
-        float bottomBorder = 1.0f - ((region.y + region.height - border.bottom) * th);
+        float topBorder = 1.0f - ((region.y + _border.top) * th);
+        float bottomBorder = 1.0f - ((region.y + region.height - _border.bottom) * th);
 
         // There are 9 sets of UVs to set.
         _uvs[TOP_LEFT].u1 = leftEdge;
@@ -784,26 +702,12 @@ namespace gameplay
         _uvs[BOTTOM_RIGHT].v2 = bottomEdge;
     }
 
-    Theme::ContainerRegion::~ContainerRegion()
-    {
-    }
-
-    const char* Theme::ContainerRegion::getId() const
-    {
-        return _id.c_str();
-    }
-
-    const Theme::Border& Theme::ContainerRegion::getBorder() const
-    {
-        return _border;
-    }
-
-    const Theme::UVs& Theme::ContainerRegion::getUVs(ContainerArea area) const
+    const Theme::UVs& Theme::Skin::getUVs(SkinArea area) const
     {
         return _uvs[area];
     }
 
-    const Vector4& Theme::ContainerRegion::getColor() const
+    const Vector4& Theme::Skin::getColor() const
     {
         return _color;
     }
@@ -811,9 +715,10 @@ namespace gameplay
     /****************
      * Theme::Style *
      ****************/
-    Theme::Style::Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
+    Theme::Style::Style(const char* id, float tw, float th,
+            const Theme::Margin& margin, const Theme::Padding& padding,
             Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled)
-        : _id(id), _margin(margin), _padding(padding)
+        : _id(id), _tw(tw), _th(th), _margin(margin), _padding(padding)
     {
         _overlays[OVERLAY_NORMAL] = normal;
         _overlays[OVERLAY_FOCUS] = focus;
@@ -821,6 +726,20 @@ namespace gameplay
         _overlays[OVERLAY_DISABLED] = disabled;
     }
 
+    Theme::Style::Style(const Style& copy)
+    {
+        _id = copy._id;
+        _margin = copy._margin;
+        _padding = copy._padding;
+        _tw = copy._tw;
+        _th = copy._th;
+
+        for (int i = 0; i < MAX_OVERLAYS; i++)
+        {
+            _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
+        }
+    }
+
     Theme::Style::~Style()
     {
         for (unsigned int i = 0; i < MAX_OVERLAYS; i++)
@@ -839,16 +758,32 @@ namespace gameplay
         return _overlays[overlayType];
     }
 
+    void Theme::Style::setMargin(float top, float bottom, float left, float right)
+    {
+        _margin.top = top;
+        _margin.bottom = bottom;
+        _margin.left = left;
+        _margin.right = right;
+    }
+
     const Theme::Margin& Theme::Style::getMargin() const
     {
         return _margin;
     }
 
+    void Theme::Style::setPadding(float top, float bottom, float left, float right)
+    {
+        _padding.top = top;
+        _padding.bottom = bottom;
+        _padding.left = left;
+        _padding.right = right;
+    }
+
     const Theme::Padding& Theme::Style::getPadding() const
     {
         return _padding;
     }
-
+    
     /*************************
      * Theme::Style::Overlay *
      *************************/
@@ -858,21 +793,123 @@ namespace gameplay
         return overlay;
     }
 
-    Theme::Style::Overlay::Overlay() : _container(NULL), _textCursor(NULL), _mouseCursor(NULL), _checkBoxIcon(NULL), _radioButtonIcon(NULL), _sliderIcon(NULL), _font(NULL)
+    Theme::Style::Overlay::Overlay() : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
     {
     }
 
+    Theme::Style::Overlay::Overlay(const Overlay& copy) : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
+    {
+        if (copy._skin)
+        {
+            _skin = new Skin(*copy._skin);
+        }
+        if (copy._cursor)
+        {
+            _cursor = new Image(*copy._cursor);
+        }
+        if (copy._imageList)
+        {
+            _imageList = new ImageList(*copy._imageList);
+        }
+
+        _font = copy._font;
+        _fontSize = copy._fontSize;
+        _alignment = copy._alignment;
+        _textRightToLeft = copy._textRightToLeft;
+        _textColor = Vector4(copy._textColor);
+        _opacity = copy._opacity;
+
+        if (_font)
+        {
+            _font->addRef();
+        }
+    }
+
     Theme::Style::Overlay::~Overlay()
     {
-        SAFE_RELEASE(_container);
-        SAFE_RELEASE(_checkBoxIcon);
-        SAFE_RELEASE(_radioButtonIcon);
-        SAFE_RELEASE(_sliderIcon);
-        SAFE_RELEASE(_mouseCursor);
-        SAFE_RELEASE(_textCursor);
+        SAFE_RELEASE(_skin);
+        SAFE_RELEASE(_imageList);
+        SAFE_RELEASE(_cursor);
         SAFE_RELEASE(_font);
     }
 
+    float Theme::Style::Overlay::getOpacity() const
+    {
+        return _opacity;
+    }
+
+    void Theme::Style::Overlay::setOpacity(float opacity)
+    {
+        _opacity = opacity;
+    }
+
+    void Theme::Style::Overlay::setBorder(float top, float bottom, float left, float right)
+    {
+        if (_skin)
+        {
+            _skin->_border.top = top;
+            _skin->_border.bottom = bottom;
+            _skin->_border.left = left;
+            _skin->_border.right = right;
+        }
+    }
+
+    const Theme::Border& Theme::Style::Overlay::getBorder() const
+    {
+        if (_skin)
+        {
+            return _skin->getBorder();
+        }
+        else
+        {
+            return Theme::Border::empty();
+        }
+    }
+
+    void Theme::Style::Overlay::setSkinColor(const Vector4& color)
+    {
+        if (_skin)
+        {
+            _skin->_color.set(color);
+        }
+    }
+
+    const Vector4& Theme::Style::Overlay::getSkinColor() const
+    {
+        if (_skin)
+        {
+            return _skin->getColor();
+        }
+
+        return Vector4::one();
+    }
+
+    void Theme::Style::Overlay::setSkinRegion(const Rectangle& region, float tw, float th)
+    {
+        assert(_skin);
+        _skin->setRegion(region, tw, th);
+    }
+
+    const Rectangle& Theme::Style::Overlay::getSkinRegion() const
+    {
+        if (_skin)
+        {
+            return _skin->getRegion();
+        }
+
+        return Rectangle::empty();
+    }
+
+    const Theme::UVs& Theme::Style::Overlay::getSkinUVs(Theme::Skin::SkinArea area) const
+    {
+        if (_skin)
+        {
+            return _skin->_uvs[area];
+        }
+
+        return UVs::empty();
+    }
+
     Font* Theme::Style::Overlay::getFont() const
     {
         return _font;
@@ -933,213 +970,260 @@ namespace gameplay
         _textColor = color;
     }
 
-    Theme::Cursor* Theme::Style::Overlay::getTextCursor() const
+    const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
     {
-        return _textCursor;
+        Image* image = _imageList->getImage(id);
+        if (image)
+        {
+            return image->getRegion();
+        }
+        else
+        {
+            return Rectangle::empty();
+        }
+    }
+    
+    void Theme::Style::Overlay::setImageRegion(const char* id, const Rectangle& region, float tw, float th)
+    {
+        Image* image = _imageList->getImage(id);
+        assert(image);
+        image->_region.set(region);
+        generateUVs(tw, th, region.x, region.y, region.width, region.height, &(image->_uvs));
     }
 
-    void Theme::Style::Overlay::setTextCursor(Theme::Cursor* cursor)
+    const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
     {
-        if (_textCursor != cursor)
+        Image* image = _imageList->getImage(id);
+        if (image)
         {
-            SAFE_RELEASE(_textCursor);
-
-            _textCursor = cursor;
-            
-            if (cursor)
-            {
-                cursor->addRef();
-            }
+            return image->getColor();
+        }
+        else
+        {
+            return Vector4::zero();
         }
     }
 
-    Theme::Cursor* Theme::Style::Overlay::getMouseCursor() const
+    void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
     {
-        return _mouseCursor;
+        Image* image = _imageList->getImage(id);
+        assert(image);
+        image->_color.set(color);
     }
 
-    void Theme::Style::Overlay::setMouseCursor(Theme::Cursor* cursor)
+    const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
     {
-        if (_mouseCursor != cursor)
+        Image* image = _imageList->getImage(id);
+        if (image)
         {
-            SAFE_RELEASE(_mouseCursor);
-
-            _mouseCursor = cursor;
-            
-            if (cursor)
-            {
-                cursor->addRef();
-            }
+            return image->getUVs();
+        }
+        else
+        {
+            return UVs::empty();
         }
     }
 
-    void Theme::Style::Overlay::setCheckBoxIcon(Icon* icon)
+    const Rectangle& Theme::Style::Overlay::getCursorRegion() const
     {
-        if (_checkBoxIcon != icon)
+        if (_cursor)
         {
-            SAFE_RELEASE(_checkBoxIcon);
+            return _cursor->getRegion();
+        }
+        else
+        {
+            return Rectangle::empty();
+        }
+    }
+    
+    void Theme::Style::Overlay::setCursorRegion(const Rectangle& region, float tw, float th)
+    {
+        assert(_cursor);
+        _cursor->_region.set(region);
+        generateUVs(tw, th, region.x, region.y, region.width, region.height, &(_cursor->_uvs));
+    }
 
-            _checkBoxIcon = icon;
-            
-            if (icon)
-            {
-                icon->addRef();
-            }
+    const Vector4& Theme::Style::Overlay::getCursorColor() const
+    {
+        if (_cursor)
+        {
+            return _cursor->getColor();
+        }
+        else
+        {
+            return Vector4::zero();
         }
     }
 
-    Theme::Icon* Theme::Style::Overlay::getCheckBoxIcon() const
+    void Theme::Style::Overlay::setCursorColor(const Vector4& color)
     {
-        return _checkBoxIcon;
+        assert(_cursor);
+        _cursor->_color.set(color);
     }
 
-    void Theme::Style::Overlay::setRadioButtonIcon(Icon* icon)
+    const Theme::UVs Theme::Style::Overlay::getCursorUVs() const
     {
-        if (_radioButtonIcon != icon)
+        if (_cursor)
+        {
+            return _cursor->getUVs();
+        }
+        else
         {
-            SAFE_RELEASE(_radioButtonIcon);
+            return UVs::empty();
+        }
+    }
 
-            _radioButtonIcon = icon;
+    void Theme::Style::Overlay::setSkin(Skin* skin)
+    {
+        if (_skin != skin)
+        {
+            SAFE_RELEASE(_skin);
+            _skin = skin;
 
-            if (icon)
+            if (skin)
             {
-                icon->addRef();
+                skin->addRef();
             }
         }
     }
 
-    Theme::Icon* Theme::Style::Overlay::getRadioButtonIcon() const
+    Theme::Skin* Theme::Style::Overlay::getSkin() const
     {
-        return _radioButtonIcon;
+        return _skin;
     }
 
-    void Theme::Style::Overlay::setSliderIcon(SliderIcon* slider)
+    void Theme::Style::Overlay::setCursor(Image* cursor)
     {
-        if (_sliderIcon != slider)
+        if (_cursor != cursor)
         {
-            SAFE_RELEASE(_sliderIcon);
+            SAFE_RELEASE(_cursor);
+            _cursor = cursor;
 
-            _sliderIcon = slider;
-
-            if (slider)
+            if (cursor)
             {
-                slider->addRef();
+                cursor->addRef();
             }
         }
     }
 
-    Theme::SliderIcon* Theme::Style::Overlay::getSliderIcon() const
+    Theme::Image* Theme::Style::Overlay::getCursor() const
     {
-        return _sliderIcon;
+        return _cursor;
     }
-
-    void Theme::Style::Overlay::setContainerRegion(ContainerRegion* container)
+            
+    void Theme::Style::Overlay::setImageList(ImageList* imageList)
     {
-        if (_container != container)
+        if (_imageList != imageList)
         {
-            SAFE_RELEASE(_container);
+            SAFE_RELEASE(_imageList);
+            _imageList = imageList;
 
-            _container = container;
-
-            if (container)
+            if (imageList)
             {
-                container->addRef();
+                imageList->addRef();
             }
         }
     }
-
-    Theme::ContainerRegion* Theme::Style::Overlay::getContainerRegion() const
+    
+    Theme::ImageList* Theme::Style::Overlay::getImageList() const
     {
-        return _container;
+        return _imageList;
     }
 
-    void Theme::generateUVs(const Texture& texture, float x, float y, float width, float height, UVs* uvs)
+    // Implementation of AnimationHandler
+    unsigned int Theme::Style::Overlay::getAnimationPropertyComponentCount(int propertyId) const
     {
-        float tw = 1.0f / texture.getWidth();
-        float th = 1.0f / texture.getHeight();
-
-        uvs->u1 = x * tw;
-        uvs->u2 = (x + width) * tw;
-        uvs->v1 = 1.0f - (y * th);
-        uvs->v2 = 1.0f - ((y + height) * th);
+        switch(propertyId)
+        {
+        case Theme::Style::Overlay::ANIMATE_OPACITY:
+            return 1;
+        default:
+            return -1;
+        }
     }
 
-    void Theme::lookUpSprites(const Properties* overlaySpace, Icon** checkBoxIcon, Icon** radioButtonIcon, SliderIcon** sliderIcon,
-                              Cursor** textCursor, Cursor** mouseCursor, ContainerRegion** containerRegion)
+    void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
     {
-        const char* checkBoxString = overlaySpace->getString("checkBox");
-        if (checkBoxString)
+        switch(propertyId)
         {
-            for (unsigned int i = 0; i < _icons.size(); i++)
-            {
-                if (strcmp(_icons[i]->getId(), checkBoxString) == 0)
-                {
-                    *checkBoxIcon = _icons[i];
-                    break;
-                }
-            }
+        case ANIMATE_OPACITY:
+            value->setFloat(0, _opacity);
+            break;
+        default:
+            break;
         }
+    }
 
-        const char* radioButtonString = overlaySpace->getString("radioButton");
-        if (radioButtonString)
+    void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+    {
+        switch(propertyId)
         {
-            for (unsigned int i = 0; i < _icons.size(); i++)
+            case ANIMATE_OPACITY:
             {
-                if (strcmp(_icons[i]->getId(), radioButtonString) == 0)
+                float opacity = value->getFloat(0);
+                if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
                 {
-                    *radioButtonIcon = _icons[i];
-                    break;
+                    _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
                 }
-            }
-        }
-
-        const char* sliderString = overlaySpace->getString("slider");
-        if (sliderString)
-        {
-            for (unsigned int i = 0; i < _sliders.size(); ++i)
-            {
-                if (strcmp(_sliders[i]->getId(), sliderString) == 0)
+                else
                 {
-                    *sliderIcon = _sliders[i];
-                    break;
+                    opacity = Curve::lerp(blendWeight, _opacity, opacity);
                 }
+                _opacity = opacity;
+                break;
             }
+            default:
+                break;
         }
+    }
+    
+    /**
+     * Theme utility methods.
+     */
+    void Theme::generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs)
+    {
+        uvs->u1 = x * tw;
+        uvs->u2 = (x + width) * tw;
+        uvs->v1 = 1.0f - (y * th);
+        uvs->v2 = 1.0f - ((y + height) * th);
+    }
 
-        const char* textCursorString = overlaySpace->getString("textCursor");
-        if (textCursorString)
+    void Theme::lookUpSprites(const Properties* overlaySpace, ImageList** imageList, Image** cursor, Skin** Skin)
+    {
+        const char* imageListString = overlaySpace->getString("imageList");
+        if (imageListString)
         {
-            for (unsigned int i = 0; i < _cursors.size(); ++i)
+            for (unsigned int i = 0; i < _imageLists.size(); ++i)
             {
-                if (strcmp(_cursors[i]->getId(), textCursorString) == 0)
+                if (strcmp(_imageLists[i]->getId(), imageListString) == 0)
                 {
-                    *textCursor = _cursors[i];
+                    *imageList = _imageLists[i];
                     break;
                 }
             }
         }
 
-        const char* mouseCursorString = overlaySpace->getString("mouseCursor");
-        if (mouseCursorString)
+        const char* cursorString = overlaySpace->getString("cursor");
+        if (cursorString)
         {
-            for (unsigned int i = 0; i < _cursors.size(); ++i)
+            for (unsigned int i = 0; i < _images.size(); ++i)
             {
-                if (strcmp(_cursors[i]->getId(), mouseCursorString) == 0)
+                if (strcmp(_images[i]->getId(), cursorString) == 0)
                 {
-                    *mouseCursor = _cursors[i];
+                    *cursor = _images[i];
                     break;
                 }
             }
         }
 
-        const char* containerString = overlaySpace->getString("container");
-        if (containerString)
+        const char* skinString = overlaySpace->getString("skin");
+        if (skinString)
         {
-            for (unsigned int i = 0; i < _containers.size(); ++i)
+            for (unsigned int i = 0; i < _skins.size(); ++i)
             {
-                if (strcmp(_containers[i]->getId(), containerString) == 0)
+                if (strcmp(_skins[i]->getId(), skinString) == 0)
                 {
-                    *containerRegion = _containers[i];
+                    *Skin = _skins[i];
                     break;
                 }
             }

+ 137 - 237
gameplay/src/Theme.h

@@ -18,7 +18,7 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  * A theme is created and stored as part of a form and represents its appearance.
  * Once loaded, the appearance properties can be retrieved from their style IDs and set on other
  * UI controls.  A Theme has one property, 'texture', which points to an image containing
- * all the Icon, Cursor, Slider, and Container sprites used by the theme.  Each set of sprites within
+ * all the Icon, Cursor, Slider, and Skin sprites used by the theme.  Each set of sprites within
  * the texture is described in its own namespace.  The rest of the Theme consists of Style namespaces.
  * A Style describes the border, margin, and padding of a Control, what icons and cursors (if any) are
  * associated with a Control, and Font properties to apply to a Control's text.
@@ -54,7 +54,7 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  *    }
  *   
  *    // Defines the border and background of a Control.
- *    container <containerID>
+ *    Skin <SkinID>
  *    {
  *        // The corners and edges of the given region will be used as border sprites.
  *        border
@@ -65,8 +65,8 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  *            right   =   <int>   // Width of right border, right corners.
  *        }
  *       
- *        region  =   <x, y, width, height>   // Total container region including entire border.
- *        color   =   <#ffffffff>             // Tint to apply to container sprites.
+ *        region  =   <x, y, width, height>   // Total Skin region including entire border.
+ *        color   =   <#ffffffff>             // Tint to apply to Skin sprites.
  *    }
  *   
  *    style <styleID>
@@ -92,7 +92,7 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
  *        // This overlay is used when a control is enabled but not active or focused.
  *        normal
  *        {
- *            container   =   <containerID>               // Container to use for border and background sprites.
+ *            Skin   =   <SkinID>               // Skin to use for border and background sprites.
  *            checkBox    =   <iconID>                    // Icon to use for checkbox sprites.
  *            radioButton =   <iconID>                    // Icon to use for radioButton sprites.
  *            slider      =   <sliderID>                  // Slider to use for slider sprites.
@@ -120,8 +120,9 @@ static const unsigned int MAX_OVERLAY_REGIONS = 9;
 class Theme: public Ref
 {
     friend class Control;
-    friend class Form;
     friend class Container;
+    friend class Form;
+    friend class Skin;
 
 public:
     class Style;
@@ -154,8 +155,14 @@ public:
     /**
      * Struct representing the UV coordinates of a rectangular image.
      */
-    typedef struct UVs
+    typedef class UVs
     {
+    public:
+        UVs();
+        UVs(float u1, float v1, float u2, float v2);
+
+        static const UVs& empty();
+
         float u1;
         float v1;
         float u2;
@@ -166,231 +173,76 @@ public:
      * Struct representing margin, border, and padding areas by
      * the width or height of each side.
      */
-    typedef struct padding
+    typedef class SideRegions
     {
+    public:
+        SideRegions() : top(0), bottom(0), left(0), right(0) {}
+
+        static const SideRegions& empty();
+
         float top;
         float bottom;
         float left;
         float right;
-
-        padding() : top(0), bottom(0), left(0), right(0) {}
     } Margin, Border, Padding;
 
-    /**
-     * An icon with two images representing "on" and "off" states.
-     */
-    class Icon : public Ref
+    class Image : public Ref
     {
         friend class Theme;
+        friend class Control;
 
     public:
-        /**
-         * Get this icon's ID.
-         *
-         * @return This icon's ID.
-         */
         const char* getId() const;
 
-        /**
-         * Get this icon's size.
-         *
-         * @return This icon's size.
-         */
-        const Vector2& getSize() const;
-        /**
-         * Get this icon's color.
-         *
-         * @return This icon's color.
-         */
-        const Vector4& getColor() const;
-
-        /**
-         * Get the UVs for this icon's off-state image.
-         *
-         * @return The UVs for this icon's off-state image.
-         */
-        const Theme::UVs& getOffUVs() const;
-
-        /**
-         * Get the UVs for this icon's on-state image.
-         *
-         * @return The UVs for this icon's on-state image.
-         */
-        const Theme::UVs& getOnUVs() const;
-
-    private:
-        Icon(const Texture& texture, const Vector2& size, const Vector2& offPosition, const Vector2& onPosition, const Vector4& color);
-        Icon(const Icon& copy);
-        ~Icon();
+        const UVs& getUVs() const;
 
-        static Icon* create(const char* id, const Texture& texture, const Vector2& size,
-                            const Vector2& offPosition, const Vector2& onPosition, const Vector4& color);
+        const Rectangle& getRegion() const;
 
-        std::string _id;
-        Vector2 _size;
-        Vector4 _color;
-        UVs _off;
-        UVs _on;
-    };
-
-    /**
-     * A set of four icons that make up a slider:
-     * two end-caps, a track, and a marker to be placed along the track.
-     */
-    class SliderIcon : public Ref
-    {
-        friend class Theme;
-
-    public:
-        /**
-         * Get this slider icon's ID.
-         *
-         * @return This slider icon's ID.
-         */
-        const char* getId() const;
-
-        /**
-         * Get the UVs for this slider's min-cap image.
-         *
-         * @return The UVs for this slider's min-cap image.
-         */
-        const Theme::UVs& getMinCapUVs() const;
-
-        /**
-         * Get the UVs for this slider's max-cap image.
-         *
-         * @return The UVs for this slider's max-cap image.
-         */
-        const Theme::UVs& getMaxCapUVs() const;
-
-        /**
-         * Get the UVs for this slider's marker image.
-         *
-         * @return The UVs for this slider's marker image.
-         */
-        const Theme::UVs& getMarkerUVs() const;
-
-        /**
-         * Get the UVs for this slider's track image.
-         *
-         * @return The UVs for this slider's track image.
-         */
-        const Theme::UVs& getTrackUVs() const;
-
-        /**
-         * Get this slider's min-cap size.
-         *
-         * @return This slider's min-cap size.
-         */
-        const Vector2& getMinCapSize() const;
-
-        /**
-         * Get this slider's max-cap size.
-         *
-         * @return This slider's max-cap size.
-         */
-        const Vector2& getMaxCapSize() const;
-
-        /**
-         * Get this slider's marker size.
-         *
-         * @return This slider's marker size.
-         */
-        const Vector2& getMarkerSize() const;
-
-        /**
-         * Get this slider's track size.
-         *
-         * @return This slider's track size.
-         */
-        const Vector2& getTrackSize() const;
-
-        /**
-         * Get this slider's color.
-         *
-         * @return This slider's color.
-         */
         const Vector4& getColor() const;
 
     private:
-        SliderIcon(const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
-                   const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color);
-        SliderIcon(const SliderIcon& copy);
-        ~SliderIcon();
+        Image(float tw, float th, const Rectangle& region, const Vector4& color);
+        ~Image();
 
-        static SliderIcon* create(const char* id, const Texture& texture, const Vector4& minCapRegion, const Vector4& maxCapRegion,
-                                  const Vector4& markerRegion, const Vector4& trackRegion, const Vector4& color);
+        static Image* create(float tw, float th, Properties* properties, const Vector4& defaultColor);
 
         std::string _id;
-        UVs _minCap;
-        UVs _maxCap;
-        UVs _marker;
-        UVs _track;
-        Vector2 _minCapSize;
-        Vector2 _maxCapSize;
-        Vector2 _markerSize;
-        Vector2 _trackSize;
+        UVs _uvs;
+        Rectangle _region;
         Vector4 _color;
     };
-    
-    /**
-     * This class represents the appearance of a cursor.
-     */
-    class Cursor : public Ref
+
+    class ImageList : public Ref
     {
         friend class Theme;
+        friend class Control;
 
     public:
-       /**
-        * Returns the Id of this Cursor.
-        *
-        * @return This cursor's ID.
-        */
         const char* getId() const;
 
-       /**
-        * Gets a UV coordinates computed from the texture region.
-        *
-        * @return This cursor's UVs.
-        */
-        const Theme::UVs& getUVs() const;
+        Image* getImage(const char* imageId) const;
 
-        /**
-         * Gets this cursor's size.
-         *
-         * @return This cursor's size.
-         */
-        const Vector2& getSize() const;
-
-        /**
-         * Gets this cursor's color.
-         *
-         * @return This cursor's color.
-         */
-        const Vector4& getColor() const;
-    
     private:
-        Cursor(const Texture& texture, const Rectangle& region, const Vector4& color);
-        Cursor(const Cursor& copy);
-        ~Cursor();
+        ImageList(const Vector4& color);
+        ImageList(const ImageList& copy);
+        ~ImageList();
 
-        static Theme::Cursor* create(const char* id, const Texture& texture, const Rectangle& region, const Vector4& color);
+        static ImageList* create(float tw, float th, Properties* properties);
 
         std::string _id;
-        UVs _uvs;
-        Vector2 _size;
+        std::vector<Image*> _images;
         Vector4 _color;
     };
 
     /**
-     * A container region defines the border and background of a control.
+     * A skin defines the border and background of a control.
      */
-    class ContainerRegion : public Ref
+    class Skin : public Ref
     {
         friend class Theme;
 
     public:
-        enum ContainerArea
+        enum SkinArea
         {
             TOP_LEFT, TOP, TOP_RIGHT,
             LEFT, CENTER, RIGHT,
@@ -398,44 +250,50 @@ public:
         };
 
         /**
-         * Gets this container area's ID.
+         * Gets this skin's ID.
          *
-         * @return This container area's ID.
+         * @return This skin's ID.
          */
         const char* getId() const;
 
         /**
-         * Gets this container area's border.
+         * Gets this skin's border.
          *
-         * @return This container area's border.
+         * @return This skin's border.
          */
         const Theme::Border& getBorder() const;
 
+        const Rectangle& getRegion() const;
+
         /**
-         * Gets this container area's UVs.
+         * Gets this skin's UVs.
          *
-         * @return This container area's UVs.
+         * @return This skin's UVs.
          */
-        const Theme::UVs& getUVs(ContainerArea area) const;
+        const Theme::UVs& getUVs(SkinArea area) const;
 
         /**
-         * Gets this container area's color.
+         * Gets this skin's color.
          *
-         * @return This container area's color.
+         * @return This skin's color.
          */
         const Vector4& getColor() const;
 
     private:
-        ContainerRegion(const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color);
-        ContainerRegion(const ContainerRegion& copy);
-        ~ContainerRegion();
+        Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+        //Skin(const Skin& copy);
+        ~Skin();
 
-        static ContainerRegion* create(const char* id, const Texture& texture, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+        static Skin* create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
+
+        void setRegion(const Rectangle& region, float tw, float th);
     
         std::string _id;
         Theme::Border _border;
         UVs _uvs[MAX_OVERLAY_REGIONS];
         Vector4 _color;
+        Rectangle _region;
+        float _tw, _th;
     };
     
     /**
@@ -447,6 +305,7 @@ public:
     class Style
     {
         friend class Theme;
+        friend class Control;
 
     public:
         class Overlay;
@@ -496,17 +355,37 @@ public:
         /**
          * This class represents a control's overlay for one of the 3 modes: normal, focussed or active.
          */
-        class Overlay : public Ref
+        class Overlay : public Ref, public AnimationTarget
         {
             friend class Theme;
             friend class Style;
 
         public:
+            /**
+             * Animate this overlay's opacity property.  Data = opacity
+             */
+            static const int ANIMATE_OPACITY = 1;
+
            /**
             * Returns the Overlay type.
             */
             OverlayType getType();
 
+            float getOpacity() const;
+            void setOpacity(float opacity);
+
+            // setBorder(const Theme::Border& border) ??
+            void setBorder(float top, float bottom, float left, float right);
+            const Border& getBorder() const;
+
+            void setSkinColor(const Vector4& color);
+            const Vector4& getSkinColor() const;
+
+            void setSkinRegion(const Rectangle& region, float tw, float th);
+            const Rectangle& getSkinRegion() const;
+
+            const Theme::UVs& getSkinUVs(Theme::Skin::SkinArea area) const;
+
            /**
             * Gets a font associated with this overlay.
             */
@@ -527,28 +406,38 @@ public:
             void setTextRightToLeft(bool rightToLeft);
 
             const Vector4& getTextColor() const;
-            void setTextColor(const Vector4& color);
-            
-           /**
-            * Gets a cursor associated with this overlay.
-            */
-            void setTextCursor(Cursor* cursor);
-            Cursor* getTextCursor() const;
-            
-            void setMouseCursor(Cursor* cursor);
-            Cursor* getMouseCursor() const;
-            
-            void setCheckBoxIcon(Icon* icon);
-            Icon* getCheckBoxIcon() const;
+            void setTextColor(const Vector4& color); 
 
-            void setRadioButtonIcon(Icon* icon);
-            Icon* getRadioButtonIcon() const;
+            const Rectangle& getImageRegion(const char* id) const;
+            void setImageRegion(const char* id, const Rectangle& region, float tw, float th);
 
-            void setSliderIcon(SliderIcon* slider);
-            SliderIcon* getSliderIcon() const;
-            
-            void setContainerRegion(ContainerRegion* container);
-            ContainerRegion* getContainerRegion() const;
+            const Vector4& getImageColor(const char* id) const;
+            void setImageColor(const char* id, const Vector4& color);
+
+            const Theme::UVs& getImageUVs(const char* id) const;
+
+            const Rectangle& getCursorRegion() const;
+            void setCursorRegion(const Rectangle& region, float tw, float th);
+
+            const Vector4& getCursorColor() const;
+            void setCursorColor(const Vector4& color);
+
+            const Theme::UVs getCursorUVs() const;
+
+            /**
+             * @see AnimationTarget#getAnimationPropertyComponentCount
+             */
+            unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+
+            /**
+             * @see AnimationTarget#getAnimationProperty
+             */
+            void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+
+            /**
+             * @see AnimationTarget#setAnimationProperty
+             */
+            void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
         
         private:
             Overlay();
@@ -557,21 +446,32 @@ public:
 
             static Overlay* create();
 
-            ContainerRegion* _container;
-            Cursor* _textCursor;
-            Cursor* _mouseCursor;
-            Icon* _checkBoxIcon;
-            Icon* _radioButtonIcon;
-            SliderIcon* _sliderIcon;
+            void setSkin(Skin* Skin);
+            Skin* getSkin() const;
+
+            void setCursor(Image* cursor);
+            Image* getCursor() const;
+            
+            void setImageList(ImageList* imageList);
+            ImageList* getImageList() const;
+
+            static const char ANIMATION_OPACITY_BIT = 0x01;
+            void applyAnimationValueOpacity(float opacity, float blendWeight);
+
+            Skin* _skin;
+            Image* _cursor;
+            ImageList* _imageList;
             Font* _font;
             unsigned int _fontSize;
             Font::Justify _alignment;
             bool _textRightToLeft;
             Vector4 _textColor;
+            float _opacity;
         };
 
     private:
-        Style(const char* id, const Theme::Margin& margin, const Theme::Padding& padding,
+        Style(const char* id, float tw, float th,
+            const Theme::Margin& margin, const Theme::Padding& padding,
             Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled);
         Style(const Style& style);
         ~Style();
@@ -580,6 +480,8 @@ public:
         Margin _margin;
         Padding _padding;
         Overlay* _overlays[MAX_OVERLAYS];
+        float _tw;
+        float _th;
     };
 
 private:
@@ -598,18 +500,16 @@ private:
      */
     ~Theme();
 
-    static void generateUVs(const Texture& texture, float x, float y, float width, float height, UVs* uvs);
-    void lookUpSprites(const Properties* overlaySpace, Icon** checkBoxIcon, Icon** radioButtonIcon, SliderIcon** sliderIcon,
-                              Cursor** textCursor, Cursor** mouseCursor, ContainerRegion** containerRegion);
+    static void generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs);
+    void lookUpSprites(const Properties* overlaySpace, ImageList** imageList, Image** mouseCursor, Skin** skin);
 
     std::string _path;
     Texture* _texture;
     SpriteBatch* _spriteBatch;
-    std::vector<Cursor*> _cursors;
     std::vector<Style*> _styles;
-    std::vector<Icon*> _icons;
-    std::vector<SliderIcon*> _sliders;
-    std::vector<ContainerRegion*> _containers;
+    std::vector<Image*> _images;
+    std::vector<ImageList*> _imageLists;
+    std::vector<Skin*> _skins;
     std::set<Font*> _fonts;
 };
 

+ 2 - 1
gameplay/src/Transform.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "Transform.h"
 #include "Game.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -780,7 +781,7 @@ void Transform::transformChanged()
     }
 }
 
-void Transform::cloneInto(Transform* transform, CloneContext &context) const
+void Transform::cloneInto(Transform* transform, NodeCloneContext &context) const
 {
     AnimationTarget::cloneInto(transform, context);
     transform->_scale.set(_scale);

+ 2 - 1
gameplay/src/Transform.h

@@ -12,6 +12,7 @@ namespace gameplay
 
 class BoundingBox;
 class BoundingSphere;
+class NodeCloneContext;
 
 /**
  * Defines a 3-dimensional transformation.
@@ -737,7 +738,7 @@ protected:
 
     void dirty();
     virtual void transformChanged();
-    void cloneInto(Transform* transform, CloneContext &context) const;
+    void cloneInto(Transform* transform, NodeCloneContext &context) const;
 
     Vector3 _scale;
     Quaternion _rotation;

+ 7 - 5
gameplay/src/VerticalLayout.cpp

@@ -45,12 +45,14 @@ namespace gameplay
     {
         // Need border, padding.
         Theme::Style* style = container->getStyle();
-        Theme::Border border;
-        Theme::ContainerRegion* containerRegion = style->getOverlay(container->getOverlayType())->getContainerRegion();
-        if (containerRegion)
+        Theme::Border border = container->getBorder(container->getState());
+        /*
+        Theme::Skin* skin = style->getOverlay(container->getOverlayType())->getSkin();
+        if (skin)
         {
-            border = containerRegion->getBorder();
+            border = skin->getBorder();
         }
+        */
         Theme::Padding padding = style->getPadding();
 
         float yPosition = 0;
@@ -80,7 +82,7 @@ namespace gameplay
 
             yPosition += margin.top;
 
-            control->setPosition(0, yPosition);
+            control->setPosition(0, yPosition, 0L);
             if (control->isDirty())
             {
                 control->update(container->getClip());

+ 0 - 86
gameplay/src/Viewport.cpp

@@ -1,86 +0,0 @@
-#include "Base.h"
-#include "Viewport.h"
-
-namespace gameplay
-{
-
-Viewport::Viewport()
-{
-}
-
-Viewport::Viewport(int x, int y, int width, int height)
-{
-    set(x, y, width, height);
-}
-
-Viewport::Viewport(const Viewport& viewport)
-{
-    set(viewport);
-}
-
-Viewport::~Viewport()
-{
-}
-
-void Viewport::set(int x, int y, int width, int height)
-{
-    _x = x;
-    _y = y;
-    _width = width;
-    _height = height;
-}
-
-void Viewport::set(const Viewport& viewport)
-{
-    _x = viewport._x;
-    _y = viewport._y;
-    _width = viewport._width;
-    _height = viewport._height;
-}
-
-int Viewport::getX() const
-{
-    return _x;
-}
-
-void Viewport::setX(int x)
-{
-    _x = x;
-}
-
-int Viewport::getY() const
-{
-    return _y;
-}
-
-void Viewport::setY(int y)
-{
-    _y = y;
-}
-
-int Viewport::getWidth() const
-{
-    return _width;
-}
-
-void Viewport::setWidth(int width)
-{
-    _width = width;
-}
-
-int Viewport::getHeight() const
-{
-    return _height;
-}
-
-void Viewport::setHeight(int height)
-{
-    _height = height;
-}
-
-void Viewport::bind()
-{
-    GL_ASSERT( glViewport(_x, _y, _width, _height) );
-}
-
-}

+ 0 - 132
gameplay/src/Viewport.h

@@ -1,132 +0,0 @@
-#ifndef VIEWPORT_H_
-#define VIEWPORT_H_
-
-namespace gameplay
-{
-
-class Camera;
-
-/**
- * Defines a rectangular viewing region used by camera the project into
- * and used by the GraphicsDevice to control the rendering region.
- */
-class Viewport
-{
-public:
-
-    /**
-     * Constructs a new viewport with all zeros.
-     */
-    Viewport();
-
-    /**
-     * Constructs a new viewport with the specified dimensions.
-     *
-     * @param x The x-coordinate of the viewport.
-     * @param y The y-coordinate of the viewport.
-     * @param width The width of the viewport.
-     * @param height The height of the viewport.
-     */
-    Viewport(int x, int y, int width, int height);
-
-    /**
-     * Constructs a new viewport from a copy.
-     *
-     * @param copy The viewport to copy.
-     */
-    Viewport(const Viewport& copy);
-
-    /**
-     * Destructor.
-     */
-    ~Viewport();
-
-    /**
-     * Sets the viewport to the specified dimensions.
-     *
-     * @param x The x-coordinate of the viewport.
-     * @param y The y-coordinate of the viewport.
-     * @param width The width of the viewport.
-     * @param height The height of the viewport.
-     */
-    void set(int x, int y, int width, int height);
-
-    /**
-     * Sets the viewport to the specified viewport copy.
-     *
-     * @param viewport The viewport to copy.
-     */
-    void set(const Viewport& viewport);
-
-    /**
-     * Gets the x-coordinate of the viewport.
-     *
-     * @return The x-coordinate of the viewport.
-     */
-    int getX() const;
-
-    /**
-     * Sets the x-coordinate of the viewport.
-     *
-     * @param x The x-coordinate of the viewport.
-     */
-    void setX(int x);
-
-    /**
-     * Gets the y-coordinate of the viewport.
-     *
-     * @return The y-coordinate of the viewport.
-     */
-    int getY() const;
-
-    /**
-     * Sets the y-coordinate of the viewport.
-     *
-     * @param y The y-coordinate of the viewport.
-     */
-    void setY(int y);
-
-    /**
-     * Gets the width of the viewport.
-     *
-     * @return The width of the viewport.
-     */
-    int getWidth() const;
-
-    /**
-     * Sets the width of the viewport.
-     *
-     * @param width The width of the viewport.
-     */
-    void setWidth(int width);
-
-    /**
-     * Gets the height of the viewport.
-     *
-     * @return The height of the viewport.
-     */
-    int getHeight() const;
-
-    /**
-     * Sets the height of the viewport.
-     *
-     * @param height The height of the viewport.
-     */
-    void setHeight(int height);
-
-    /**
-     * Makes this the active viewport for rendering.
-     */
-    void bind();
-
-private:
-
-    int _x;
-    int _y;
-    int _width;
-    int _height;
-};
-
-}
-
-#endif