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

Merge pull request #15 from blackberry-gaming/next

Next
Sean Paul Taylor 14 роки тому
батько
коміт
5f2781d54a
38 змінених файлів з 1335 додано та 473 видалено
  1. 1 0
      gameplay-encoder/gameplay-encoder.vcxproj
  2. 3 0
      gameplay-encoder/gameplay-encoder.vcxproj.filters
  3. 81 37
      gameplay-encoder/src/Quaternion.cpp
  4. 95 39
      gameplay-encoder/src/Quaternion.h
  5. 19 0
      gameplay-encoder/src/Quaternion.inl
  6. 5 5
      gameplay-template/src/TemplateGame.cpp
  7. 1 1
      gameplay-template/src/TemplateGame.h
  8. 1 0
      gameplay/gameplay.vcxproj
  9. 3 0
      gameplay/gameplay.vcxproj.filters
  10. 19 167
      gameplay/src/AnimationClip.cpp
  11. 1 1
      gameplay/src/AnimationClip.h
  12. 17 3
      gameplay/src/AnimationController.cpp
  13. 4 2
      gameplay/src/AnimationController.h
  14. 1 1
      gameplay/src/AnimationTarget.cpp
  15. 4 3
      gameplay/src/AnimationTarget.h
  16. 7 9
      gameplay/src/AnimationValue.cpp
  17. 2 3
      gameplay/src/AnimationValue.h
  18. 3 3
      gameplay/src/BoundingBox.cpp
  19. 3 10
      gameplay/src/Curve.cpp
  20. 77 0
      gameplay/src/FileSystem.cpp
  21. 10 0
      gameplay/src/FileSystem.h
  22. 9 0
      gameplay/src/Game.cpp
  23. 24 0
      gameplay/src/Game.h
  24. 145 18
      gameplay/src/MaterialParameter.cpp
  25. 5 1
      gameplay/src/MaterialParameter.h
  26. 0 2
      gameplay/src/Matrix.cpp
  27. 39 0
      gameplay/src/Mouse.h
  28. 136 81
      gameplay/src/PhysicsController.cpp
  29. 20 9
      gameplay/src/PhysicsController.h
  30. 79 27
      gameplay/src/PhysicsRigidBody.cpp
  31. 17 0
      gameplay/src/PhysicsRigidBody.h
  32. 105 2
      gameplay/src/PlatformQNX.cpp
  33. 27 3
      gameplay/src/PlatformWin32.cpp
  34. 42 28
      gameplay/src/Quaternion.cpp
  35. 30 2
      gameplay/src/Quaternion.h
  36. 267 15
      gameplay/src/Transform.cpp
  37. 32 1
      gameplay/src/Transform.h
  38. 1 0
      gameplay/src/gameplay.h

+ 1 - 0
gameplay-encoder/gameplay-encoder.vcxproj

@@ -98,6 +98,7 @@
     <ClInclude Include="src\VertexElement.h" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="src\Quaternion.inl" />
     <None Include="src\Vector2.inl" />
     <None Include="src\Vector3.inl" />
     <None Include="src\Vector4.inl" />

+ 3 - 0
gameplay-encoder/gameplay-encoder.vcxproj.filters

@@ -263,6 +263,9 @@
     <None Include="src\Vector4.inl">
       <Filter>src</Filter>
     </None>
+    <None Include="src\Quaternion.inl">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="src">

+ 81 - 37
gameplay-encoder/src/Quaternion.cpp

@@ -19,6 +19,10 @@ Quaternion::Quaternion(float* array)
     set(array);
 }
 
+Quaternion::Quaternion(const Vector3& axis, float angle)
+{
+    set(axis, angle);
+}
 
 Quaternion::Quaternion(const Quaternion& copy)
 {
@@ -31,14 +35,14 @@ Quaternion::~Quaternion()
 
 const Quaternion& Quaternion::identity()
 {
-    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
-    return *value;
+    static Quaternion value(0.0f, 0.0f, 0.0f, 1.0f);
+    return value;
 }
 
 const Quaternion& Quaternion::zero()
 {
-    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 0.0f);
-    return *value;
+    static Quaternion value(0.0f, 0.0f, 0.0f, 0.0f);
+    return value;
 }
 
 bool Quaternion::isIdentity() const
@@ -51,13 +55,6 @@ bool Quaternion::isZero() const
     return x == 0.0f && y == 0.0f && z == 0.0f && z == 0.0f;
 }
 
-void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
-{
-    assert(dst);
-
-    m.decompose(NULL, dst, NULL);
-}
-
 void Quaternion::createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst)
 {
     assert(dst);
@@ -154,12 +151,12 @@ void Quaternion::normalize(Quaternion* dst) const
 
     float n = x * x + y * y + z * z + w * w;
 
-    // already normalized
+    // Already normalized.
     if (n == 1.0f)
         return;
 
     n = sqrt(n);
-    // too close to zero
+    // Too close to zero.
     if (n < 0.000001f)
         return;
 
@@ -188,6 +185,11 @@ void Quaternion::set(float* array)
     w = array[3];
 }
 
+void Quaternion::set(const Vector3& axis, float angle)
+{
+    Quaternion::createFromAxisAngle(axis, angle, this);
+}
+
 void Quaternion::set(const Quaternion& q)
 {
     this->x = q.x;
@@ -223,6 +225,17 @@ void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quate
     assert(dst);
     assert(!(t < 0.0f || t > 1.0f));
 
+    if (t == 0.0f)
+    {
+        memcpy(dst, &q1, sizeof(float) * 4);
+        return;
+    }
+    else if (t == 1.0f)
+    {
+        memcpy(dst, &q2, sizeof(float) * 4);
+        return;
+    }
+
     float t1 = 1.0f - t;
 
     dst->x = t1 * q1.x + t * q2.x;
@@ -232,21 +245,65 @@ void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quate
 }
 
 void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
+{
+    slerp(q1.x, q1.y, q1.z, q1.w, q2.x, q2.y, q2.z, q2.w, t, &dst->x, &dst->y, &dst->z, &dst->w);
+}
+
+void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
+{
+    assert(dst);
+    assert(!(t < 0.0f || t > 1.0f));
+
+    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
+    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
+
+    slerpForSquad(q1, q2, t, &dstQ);
+    slerpForSquad(s1, s2, t, &dstS);
+    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+}
+
+void Quaternion::slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw)
 {
     // Fast slerp implementation by kwhatmough:
     // It contains no division operations, no trig, no inverse trig
     // and no sqrt. Not only does this code tolerate small constraint
     // errors in the input quaternions, it actually corrects for them.
-    assert(dst);
+    assert(dstx && dsty && dstz && dstw);
     assert(!(t < 0.0f || t > 1.0f));
 
+    if (t == 0.0f)
+    {
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
+        return;
+    }
+    else if (t == 1.0f)
+    {
+        *dstx = q2x;
+        *dsty = q2y;
+        *dstz = q2z;
+        *dstw = q2w;
+        return;
+    }
+
+    if (q1x == q2x && q1y == q2y && q1z == q2z && q1w == q2w)
+    {
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
+        return;
+    }
+
     float halfY, alpha, beta;
     float u, f1, f2a, f2b;
     float ratio1, ratio2;
     float halfSecHalfTheta, versHalfTheta;
     float sqNotU, sqU;
 
-    float cosTheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+    float cosTheta = q1w * q2w + q1x * q2x + q1y * q2y + q1z * q2z;
 
     // As usual in all slerp implementations, we fold theta.
     alpha = cosTheta >= 0 ? 1.0f : -1.0f;
@@ -287,34 +344,21 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
     beta = f1 + f2b;
 
     // Apply final coefficients to a and b as usual.
-    float w = alpha * q1.w + beta * q2.w;
-    float x = alpha * q1.x + beta * q2.x;
-    float y = alpha * q1.y + beta * q2.y;
-    float z = alpha * q1.z + beta * q2.z;
+    float w = alpha * q1w + beta * q2w;
+    float x = alpha * q1x + beta * q2x;
+    float y = alpha * q1y + beta * q2y;
+    float z = alpha * q1z + beta * q2z;
 
     // This final adjustment to the quaternion's length corrects for
-    // any small constraint error in the inputs q1 and q2. But as you
+    // any small constraint error in the inputs q1 and q2 But as you
     // can see, it comes at the cost of 9 additional multiplication
     // operations. If this error-correcting feature is not required,
     // the following code may be removed.
     f1 = 1.5f - 0.5f * (w * w + x * x + y * y + z * z);
-    dst->w = w * f1;
-    dst->x = x * f1;
-    dst->y = y * f1;
-    dst->z = z * f1;
-}
-
-void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
-{
-    assert(dst);
-    assert(!(t < 0.0f || t > 1.0f));
-
-    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
-    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
-
-    slerpForSquad(q1, q2, t, &dstQ);
-    slerpForSquad(s1, s2, t, &dstS);
-    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+    *dstw = w * f1;
+    *dstx = x * f1;
+    *dsty = y * f1;
+    *dstz = z * f1;
 }
 
 void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)

+ 95 - 39
gameplay-encoder/src/Quaternion.h

@@ -20,41 +20,46 @@ class Matrix;
  * lerp (linear interpolation): the interpolation curve gives a straight line in quaternion space. It is simple and fast to compute. The only problem is that it does not provide constant angular velocity. Note that a constant velocity is not necessarily a requirement for a curve;
  * slerp (spherical linear interpolation): the interpolation curve forms a great arc on the quaternion unit sphere. Slerp provides constant angular velocity;
  * squad (spherical spline interpolation): interpolating between a series of rotations using slerp leads to the following problems:
- * the curve is not smooth at the control points;
- * the angular velocity is not constant;
- * the angular velocity is not continuous at the control points.
+ * - the curve is not smooth at the control points;
+ * - the angular velocity is not constant;
+ * - the angular velocity is not continuous at the control points.
  *
  * Since squad is continuously differentiable, it remedies the first and third problems mentioned above.
  * The slerp method provided here is intended for interpolation of principal rotations. It treats +q and -q as the same principal rotation and is at liberty to use the negative of either input. The resulting path is always the shorter arc.
  *
  * The lerp method provided here interpolates strictly in quaternion space. Note that the resulting path may pass through the origin if interpolating between a quaternion and its exact negative.
  *
- * As an example, consider the following quaternions
+ * As an example, consider the following quaternions:
  *
  * q1 = (0.6, 0.8, 0.0, 0.0),
  * q2 = (0.0, 0.6, 0.8, 0.0),
  * q3 = (0.6, 0.0, 0.8, 0.0), and
  * q4 = (-0.8, 0.0, -0.6, 0.0).
  * For the point p = (1.0, 1.0, 1.0), the following figures show the trajectories of p using lerp, slerp, and squad.
- *
- * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/LERP.PNG"
- * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/SLERP.PNG"
- * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/SQUAD.PNG"
  */
 class Quaternion
 {
+    friend class Curve;
+
 public:
 
-    /** The x-value of the quaternion's vector component. */
+    /**
+     * The x-value of the quaternion's vector component.
+     */
     float x;
-    /** The y-value of the quaternion's vector component. */
+    /**
+     * The y-value of the quaternion's vector component.
+     */
     float y;
-    /** The z-value of the quaternion's vector component. */
+    /**
+     * The z-value of the quaternion's vector component.
+     */
     float z;
-    /** The scalar component of the quaternion. */
+    /**
+     * The scalar component of the quaternion.
+     */
     float w;
 
-
     /**
      * Constructs a quaternion initialized to (0, 0, 0, 1).
      */
@@ -73,14 +78,22 @@ public:
     /**
      * Constructs a new quaternion from the values in the specified array.
      *
-     * @param array
+     * @param array The values for the new quaternion.
      */
     Quaternion(float* array);
 
+    /**
+     * Constructs a quaternion equal to the rotation from the specified axis and angle.
+     *
+     * @param axis A vector describing the axis of rotation.
+     * @param angle The angle of rotation (in radians).
+     */
+    Quaternion(const Vector3& axis, float angle);
+
     /**
      * Constructs a new quaternion that is a copy of the specified one.
      *
-     * @param copy The quaternion to copy
+     * @param copy The quaternion to copy.
      */
     Quaternion(const Quaternion& copy);
 
@@ -92,7 +105,7 @@ public:
     /**
      * Returns the identity quaternion.
      *
-     * @return The quaternion.
+     * @return The identity quaternion.
      */
     static const Quaternion& identity();
 
@@ -104,34 +117,25 @@ public:
     static const Quaternion& zero();
 
     /**
-     * Determines if this quaterion is equal to the identity quaternion.
+     * Determines if this quaternion is equal to the identity quaternion.
      *
-     * @return true if the identity, false otherwise.
+     * @return true if it is the identity quaternion, false otherwise.
      */
     bool isIdentity() const;
 
     /**
-     * Determines if this quaterion is all zeros.
+     * Determines if this quaternion is all zeros.
      *
-     * @return true if zeros, false otherwise.
+     * @return true if this quaternion is all zeros, false otherwise.
      */
     bool isZero() const;
 
-    /**
-     * Create a quaternion equal to the rotational part of the specified matrix
-     * and stores the result in dst.
-     *
-     * @param m The matrix.
-     * @param dst A quaternion to store the conjugate in.
-     */
-    static void createFromRotationMatrix(const Matrix& m, Quaternion* dst);
-
     /**
      * Creates this quaternion equal to the rotation from the specified axis and angle
-     * and store the result in dst.
+     * and stores the result in dst.
      *
      * @param axis A vector describing the axis of rotation.
-     * @param angle The angle of rotation, in radians.
+     * @param angle The angle of rotation (in radians).
      * @param dst A quaternion to store the conjugate in.
      */
     static void createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst);
@@ -226,6 +230,14 @@ public:
      */
     void set(float* array);
 
+    /**
+     * Sets the quaternion equal to the rotation from the specified axis and angle.
+     * 
+     * @param axis The axis of rotation.
+     * @param angle The angle of rotation (in radians).
+     */
+    void set(const Vector3& axis, float angle);
+
     /**
      * Sets the elements of this quaternion to a copy of the specified quaternion.
      *
@@ -256,10 +268,10 @@ public:
      * @param q1 The first quaternion.
      * @param q2 The second quaternion.
      * @param t The interpolation coefficient.
-     * @param dst A quaternion to store the result in
+     * @param dst A quaternion to store the result in.
      */
     static void lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates between two quaternions using spherical linear interpolation.
      *
@@ -273,10 +285,10 @@ public:
      * @param q1 The first quaternion.
      * @param q2 The second quaternion.
      * @param t The interpolation coefficient.
-     * @param dst A quaternion to store the result in
+     * @param dst A quaternion to store the result in.
      */
     static void slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates over a series of quaternions using spherical spline interpolation.
      *
@@ -289,20 +301,64 @@ public:
      *
      * @param q1 The first quaternion.
      * @param q2 The second quaternion.
-     * @param s1 The first control point
-     * @param s2 The second control point
+     * @param s1 The first control point.
+     * @param s2 The second control point.
      * @param t The interpolation coefficient.
-     * @param dst A quaternion to store the result in
+     * @param dst A quaternion to store the result in.
      */
     static void squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst);
 
+    /**
+     * Calculates the quaternion product of this quaternion with the given quaternion.
+     * 
+     * Note: this does not modify this quaternion.
+     * 
+     * @param q The quaternion to multiply.
+     * @return The quaternion product.
+     */
+    inline Quaternion operator*(const Quaternion& q) const;
+
+    /**
+     * Multiplies this quaternion with the given quaternion.
+     * 
+     * @param q The quaternion to multiply.
+     * @return This quaternion, after the multiplication occurs.
+     */
+    inline Quaternion& operator*=(const Quaternion& q);
 
 private:
 
-    static void slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
+    /**
+     * Interpolates between two quaternions using spherical linear interpolation.
+     *
+     * Spherical linear interpolation provides smooth transitions between different
+     * orientations and is often useful for animating models or cameras in 3D.
+     *
+     * Note: For accurate interpolation, the input quaternions must be at (or close to) unit length.
+     * This method does not automatically normalize the input quaternions, so it is up to the
+     * caller to ensure they call normalize beforehand, if necessary.
+     *
+     * @param q1x The x component of the first quaternion.
+     * @param q1y The y component of the first quaternion.
+     * @param q1z The z component of the first quaternion.
+     * @param q1w The w component of the first quaternion.
+     * @param q2x The x component of the second quaternion.
+     * @param q2y The y component of the second quaternion.
+     * @param q2z The z component of the second quaternion.
+     * @param q2w The w component of the second quaternion.
+     * @param t The interpolation coefficient.
+     * @param dstx A pointer to store the x component of the slerp in.
+     * @param dsty A pointer to store the y component of the slerp in.
+     * @param dstz A pointer to store the z component of the slerp in.
+     * @param dstw A pointer to store the w component of the slerp in.
+     */
+    static void slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw);
 
+    static void slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
 };
 
 }
 
+#include "Quaternion.inl"
+
 #endif

+ 19 - 0
gameplay-encoder/src/Quaternion.inl

@@ -0,0 +1,19 @@
+#include "Quaternion.h"
+
+namespace gameplay
+{
+
+inline Quaternion Quaternion::operator*(const Quaternion& q) const
+{
+    Quaternion result(*this);
+    result.multiply(q);
+    return result;
+}
+
+inline Quaternion& Quaternion::operator*=(const Quaternion& q)
+{
+    multiply(q);
+    return *this;
+}
+
+}

+ 5 - 5
gameplay-template/src/TemplateGame.cpp

@@ -58,15 +58,15 @@ bool TemplateGame::drawScene(Node* node, void* cookie)
     return true;
 }
 
-void TemplateGame::touch(int x, int y, int touchEvent)
+void TemplateGame::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-    switch (touchEvent)
+    switch (evt)
     {
-    case Input::TOUCHEVENT_PRESS:
+    case Touch::TOUCH_PRESS:
         break;
-    case Input::TOUCHEVENT_RELEASE:
+    case Touch::TOUCH_RELEASE:
         break;
-    case Input::TOUCHEVENT_MOVE:
+    case Touch::TOUCH_MOVE:
         break;
     };
 }

+ 1 - 1
gameplay-template/src/TemplateGame.h

@@ -20,7 +20,7 @@ public:
     /**
      * Touch event handler.
      */
-    void touch(int x, int y, int touchEvent);
+    void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
 protected:
 

+ 1 - 0
gameplay/gameplay.vcxproj

@@ -115,6 +115,7 @@
     <ClInclude Include="src\Light.h" />
     <ClInclude Include="src\Material.h" />
     <ClInclude Include="src\MeshBatch.h" />
+    <ClInclude Include="src\Mouse.h" />
     <ClInclude Include="src\Pass.h" />
     <ClInclude Include="src\MaterialParameter.h" />
     <ClInclude Include="src\Matrix.h" />

+ 3 - 0
gameplay/gameplay.vcxproj.filters

@@ -434,6 +434,9 @@
     <ClInclude Include="src\MeshBatch.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\Mouse.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 19 - 167
gameplay/src/AnimationClip.cpp

@@ -198,7 +198,7 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
     _endListeners->push_back(listener);
 }
 
-bool AnimationClip::update(unsigned long elapsedTime)
+bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets)
 {
     float speed = _speed;
     if (!_isPlaying)
@@ -295,152 +295,32 @@ bool AnimationClip::update(unsigned long elapsedTime)
         target = channel->_target;
         value = _values[i];
 
-        // Get the current value.
-        target->getAnimationPropertyValue(channel->_propertyId, value);
+        // If the target's _animationPropertyBitFlag is clear, we can assume that this is the first
+        // animation channel to act on the target and we can add the target to the list of
+        // active targets stored by the AnimationController.
+        if (target->_animationPropertyBitFlag == 0x00)
+            activeTargets->push_front(target);
 
-        bool isHighest = false;
-        // My channel priority has changed if my priority is greater than the active animation count.
-        if (!target->_highestPriority)
-        {
-            target->_highestPriority = channel;
-            value->_isFirstActing = true;
-        }
-
-        if (_blendWeight != 0.0f)
-        {
-            // Evaluate point on Curve.
-            channel->_curve->evaluate(percentComplete, value->_interpolatedValue);
-
-            if (!channel->_curve->_quaternionOffset)
-            {
-                if (value->_isFirstActing)
-                {
-                    unsigned int componentCount = value->_componentCount;
-                    for (unsigned int j = 0; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] = value->_interpolatedValue[j];
-                    }
-                }
-                else
-                {
-                    unsigned int componentCount = value->_componentCount;
-                    for (unsigned int j = 0; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] += value->_interpolatedValue[j];
-                    }
-                }
-            }
-            else
-            {   //We have Quaternions!!!
-                unsigned int quaternionOffset = *(channel->_curve->_quaternionOffset);
-                
-                if (value->_isFirstActing)
-                {
-                    unsigned int j = 0;
-                    for (; j < quaternionOffset; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] = value->_interpolatedValue[j];
-                    }
-
-                    // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
-                    Quaternion* interpolatedQuaternion = (Quaternion*) (value->_interpolatedValue + j);
-                    Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + j);
-
-                    // If we have a blend weight, we apply it by slerping from the identity to our interpolated value at the given weight.
-                    if (_blendWeight != 1.0f)
-                        Quaternion::slerp(Quaternion::identity(), *interpolatedQuaternion, _blendWeight, interpolatedQuaternion);
-                    
-                    // Add in contribution.
-                    currentQuaternion->set(*interpolatedQuaternion);
-                    
-                    unsigned int componentCount = value->_componentCount;
-                    for (j += 4; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] = value->_interpolatedValue[j];
-                    }
-                }
-                else
-                {
-                    unsigned int j = 0;
-                    for (; j < quaternionOffset; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] += value->_interpolatedValue[j];
-                    }
-                    // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
-
-                    Quaternion* interpolatedQuaternion = (Quaternion*) (value->_interpolatedValue + j);
-                    Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + j);
-
-                    // If we have a blend weight, we apply it by slerping from the identity to our interpolated value at the given weight.
-                    if (_blendWeight != 1.0f)
-                        Quaternion::slerp(Quaternion::identity(), *interpolatedQuaternion, _blendWeight, interpolatedQuaternion);
-                    
-                    // Add in contribution.
-                    currentQuaternion->multiply(*interpolatedQuaternion);
-                    
-                    unsigned int componentCount = value->_componentCount;
-                    for (j += 4; j < componentCount; j++)
-                    {
-                        if (_blendWeight != 1.0f)
-                            value->_interpolatedValue[j] *= _blendWeight;
-
-                        value->_currentValue[j] += value->_interpolatedValue[j];
-                    }
-                }
-            }
-        }
-        else if (value->_isFirstActing)
-        {
-            if (!channel->_curve->_quaternionOffset)
-            {
-                memset(value->_currentValue, 0.0f, value->_componentCount);
-            }
-            else
-            {
-                unsigned int quaternionOffset = *(channel->_curve->_quaternionOffset);
-                unsigned int j = 0;
-                for (; j < quaternionOffset; j++)
-                {
-                    value->_currentValue[j] = 0.0f;
-                }
-
-                // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
-                Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + j);
-
-                // Set it to identity.
-                currentQuaternion->setIdentity();
-                
-                unsigned int componentCount = value->_componentCount;
-                for (j += 4; j < componentCount; j++)
-                {
-                    value->_currentValue[j] = 0.0f;
-                }
-            }
-        }
-        
+        // Evaluate the point on Curve
+        channel->_curve->evaluate(percentComplete, value->_value);
         // Set the animation value on the target property.
-        target->setAnimationPropertyValue(channel->_propertyId, value);
+        target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
     }
 
     // When ended. Probably should move to it's own method so we can call it when the clip is ended early.
     if (!_isPlaying)
     {
         onEnd();
+        // Notify end listeners if any.
+        if (_endListeners)
+        {
+            std::vector<Listener*>::iterator listener = _endListeners->begin();
+            while (listener != _endListeners->end())
+            {
+                (*listener)->animationEvent(this, Listener::END);
+                listener++;
+            }
+        }
     }
 
     return !_isPlaying;
@@ -475,36 +355,8 @@ void AnimationClip::onBegin()
 
 void AnimationClip::onEnd()
 {
-    AnimationValue* value;
-    Animation::Channel* channel = NULL;
-    AnimationTarget* target = NULL;
-    unsigned int channelCount = _animation->_channels.size();
-    for (unsigned int i = 0; i < channelCount; i++)
-    {
-        value = _values[i];
-
-        if (value->_isFirstActing)
-        {
-            channel = _animation->_channels[i];
-            target = channel->_target;
-            target->_highestPriority = NULL;
-            value->_isFirstActing = false;
-        }
-    }
-
     _blendWeight = 1.0f;
     _timeStarted = 0;
-
-    // Notify end listeners if any.
-    if (_endListeners)
-    {
-        std::vector<Listener*>::iterator listener = _endListeners->begin();
-        while (listener != _endListeners->end())
-        {
-            (*listener)->animationEvent(this, Listener::END);
-            listener++;
-        }
-    }
 }
 
 }

+ 1 - 1
gameplay/src/AnimationClip.h

@@ -215,7 +215,7 @@ private:
     /**
      * Updates the animation with the elapsed time.
      */
-    bool update(unsigned long elapsedTime);
+    bool update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets);
 
     /**
      * Handles when the AnimationClip begins.

+ 17 - 3
gameplay/src/AnimationController.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 {
 
 AnimationController::AnimationController()
-    : _state(IDLE), _animations(NULL)
+    : _state(STOPPED), _animations(NULL)
 {
 }
 
@@ -66,6 +66,7 @@ Animation* AnimationController::createAnimationFromTo(const char* id, AnimationT
 
     Animation* animation = createAnimation(id, target, propertyId, 2, keyTimes, keyValues, type);
 
+    SAFE_DELETE_ARRAY(keyValues);
     SAFE_DELETE_ARRAY(keyTimes);
     
     return animation;
@@ -85,6 +86,7 @@ Animation* AnimationController::createAnimationFromBy(const char* id, AnimationT
 
     Animation* animation = createAnimation(id, target, propertyId, 2, keyTimes, keyValues, type);
 
+    SAFE_DELETE_ARRAY(keyValues);
     SAFE_DELETE_ARRAY(keyTimes);
 
     return animation;
@@ -112,6 +114,7 @@ void AnimationController::stopAllAnimations()
         clip->_isPlaying = false;
         clip->onEnd();
         SAFE_RELEASE(clip);
+        clipIter++;
     }
     _runningClips.clear();
 
@@ -269,7 +272,7 @@ void AnimationController::initialize()
 void AnimationController::finalize()
 {
     stopAllAnimations();
-    _state = PAUSED;
+    _state = STOPPED;
 }
 
 void AnimationController::resume()
@@ -330,11 +333,12 @@ void AnimationController::update(long elapsedTime)
     if (_state != RUNNING)
         return;
 
+    // Loop through running clips and call update() on them.
     std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
     while (clipIter != _runningClips.end())
     {
         AnimationClip* clip = (*clipIter);
-        if (clip->update(elapsedTime))
+        if (clip->update(elapsedTime, &_activeTargets))
         {
             SAFE_RELEASE(clip);
             clipIter = _runningClips.erase(clipIter);
@@ -344,6 +348,16 @@ void AnimationController::update(long elapsedTime)
             clipIter++;
         }
     }
+
+    // Loop through active AnimationTarget's and reset their _animationPropertyBitFlag for the next frame.
+    std::list<AnimationTarget*>::iterator targetItr = _activeTargets.begin();
+    while (targetItr != _activeTargets.end())
+    {
+        AnimationTarget* target = (*targetItr);
+        target->_animationPropertyBitFlag = 0x00;
+        targetItr++;
+    }
+    _activeTargets.clear();
     
     if (_runningClips.empty())
         _state = IDLE;

+ 4 - 2
gameplay/src/AnimationController.h

@@ -117,7 +117,8 @@ private:
     {
         RUNNING,
         IDLE,
-        PAUSED
+        PAUSED,
+        STOPPED
     };
 
     /**
@@ -204,7 +205,8 @@ private:
     void destroyAllAnimations();
     
     State _state;                               // The current state of the AnimationController.
-    std::list<AnimationClip*> _runningClips;    // A list of currently running AnimationClips.
+    std::list<AnimationClip*> _runningClips;    // A list of running AnimationClips.
+    std::list<AnimationTarget*> _activeTargets;   // A list of animating AnimationTargets.
     std::vector<Animation*> _animations;        // A list of animations registered with the AnimationController
 };
 

+ 1 - 1
gameplay/src/AnimationTarget.cpp

@@ -8,7 +8,7 @@ namespace gameplay
 {
 
 AnimationTarget::AnimationTarget()
-    : _targetType(SCALAR), _highestPriority(NULL), _animationChannels(NULL)
+    : _targetType(SCALAR), _animationPropertyBitFlag(0x00), _animationChannels(NULL)
 {
 }
 

+ 4 - 3
gameplay/src/AnimationTarget.h

@@ -45,7 +45,7 @@ public:
      * @param propertyId The ID of the property on the AnimationTarget to set the animation property value on.
      * @param value The container to set the animation property value in.
      */
-    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value) = 0;
+    virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f) = 0;
 
 protected:
     
@@ -69,6 +69,8 @@ protected:
 
     TargetType _targetType;             // The type of target this is.
 
+    char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.
+
 private:
 
     /**
@@ -86,9 +88,8 @@ private:
      */
     static int getPropertyId(TargetType type, const char* propertyIdStr);
 
-    Animation::Channel* _highestPriority;
     std::vector<Animation::Channel*>* _animationChannels;   // Collection of all animation channels that target the AnimationTarget
-
+    
 };
 }
 

+ 7 - 9
gameplay/src/AnimationValue.cpp

@@ -5,44 +5,42 @@ namespace gameplay
 {
 
 AnimationValue::AnimationValue(unsigned int componentCount)
-  : _isFirstActing(false), _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
+  : _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
 {
-    _currentValue = new float[_componentCount];
-    _interpolatedValue = new float[_componentCount];
+    _value = new float[_componentCount];
 }
 
 AnimationValue::~AnimationValue()
 {
-    SAFE_DELETE_ARRAY(_currentValue);
-    SAFE_DELETE_ARRAY(_interpolatedValue);
+    SAFE_DELETE_ARRAY(_value);
 }
 
 float AnimationValue::getFloat(unsigned int index) const
 {
     assert(index < _componentCount);
 
-    return _currentValue[index];
+    return _value[index];
 }
 
 void AnimationValue::setFloat(unsigned int index, float value)
 {
     assert(index < _componentCount);
 
-    _currentValue[index] = value;
+    _value[index] = value;
 }
 
 void AnimationValue::getFloat(float* value, unsigned int offset, unsigned int length) const
 {
     assert(value && offset < _componentCount && (offset + length) <= _componentCount);
 
-    memcpy(value + offset, _currentValue, length * sizeof(float));
+    memcpy(value + offset, _value, length * sizeof(float));
 }
 
 void AnimationValue::setFloat(float* value, unsigned int offset, unsigned int length)
 {
     assert(value && offset < _componentCount && (offset + length) <= _componentCount);
 
-    memcpy(_currentValue, value + offset, length * sizeof(float));
+    memcpy(_value, value + offset, length * sizeof(float));
 }
 
 }

+ 2 - 3
gameplay/src/AnimationValue.h

@@ -72,11 +72,10 @@ private:
      */
     ~AnimationValue();
 
-    bool _isFirstActing;            // Flag indicating if this value's channel is the first to act on the target.
     unsigned int _componentCount;   // The number of float values for the property.
     unsigned int _componentSize;    // The number of bytes of memory the property is.
-    float* _currentValue;           // The current value of the property.
-    float* _interpolatedValue;      // The last interpolated value of the property.
+    float* _value;                  // The current value of the property.
+
 };
 
 }

+ 3 - 3
gameplay/src/BoundingBox.cpp

@@ -69,9 +69,9 @@ bool BoundingBox::intersects(const BoundingSphere& sphere) const
 
 bool BoundingBox::intersects(const BoundingBox& box) const
 {
-    return ((min.x >= box.min.x && min.x <= max.x) || (box.min.x >= min.x && box.min.x <= max.x)) &&
-            ((min.y >= box.min.y && min.y <= max.y) || (box.min.y >= min.y && box.min.y <= max.y)) &&
-            ((min.z >= box.min.z && min.z <= max.z) || (box.min.z >= min.z && box.min.z <= max.z));
+    return ((min.x >= box.min.x && min.x <= box.max.x) || (box.min.x >= min.x && box.min.x <= max.x)) &&
+            ((min.y >= box.min.y && min.y <= box.max.y) || (box.min.y >= min.y && box.min.y <= max.y)) &&
+            ((min.z >= box.min.z && min.z <= box.max.z) || (box.min.z >= min.z && box.min.z <= max.z));
 }
 
 bool BoundingBox::intersects(const Frustum& frustum) const

+ 3 - 10
gameplay/src/Curve.cpp

@@ -1109,19 +1109,12 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
 }
 
 void Curve::interpolateQuaternion(float s, float* from, float* to, float* dst) const
-{
-    Quaternion quatFrom(from);
-    Quaternion quatTo(to);
-
-    // Normalize the quaternions.
-    quatFrom.normalize();
-    quatTo.normalize();
-        
+{        
     // Evaluate.
     if (s >= 0)
-        Quaternion::slerp(quatFrom, quatTo, s, (Quaternion*)dst);
+        Quaternion::slerp(from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], s, dst, dst + 1, dst + 2, dst + 3);
     else
-        Quaternion::slerp(quatTo, quatFrom, -s, (Quaternion*)dst);
+        Quaternion::slerp(to[0], to[1], to[2], to[3], from[0], from[1], from[2], from[3], s, dst, dst + 1, dst + 2, dst + 3);
 }
 
 int Curve::determineIndex(float time) const

+ 77 - 0
gameplay/src/FileSystem.cpp

@@ -1,6 +1,14 @@
 #include "Base.h"
 #include "FileSystem.h"
 
+#ifdef WIN32
+    #include <windows.h>
+    #include <tchar.h>
+    #include <stdio.h>
+#else
+    #include <dirent.h>
+#endif
+
 namespace gameplay
 {
 
@@ -24,6 +32,75 @@ const char* FileSystem::getResourcePath()
     return __resourcePath.c_str();
 }
 
+bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
+{
+    // TODO make this method work with absolute and relative paths.
+#ifdef WIN32
+    std::string path(FileSystem::getResourcePath());
+    if (dirPath && strlen(dirPath) > 0)
+    {
+        path.append(dirPath);
+    }
+    path.append("/*");
+    // Convert char to wchar
+    std::basic_string<TCHAR> wPath;
+    wPath.assign(path.begin(), path.end());
+
+    WIN32_FIND_DATA FindFileData;
+    HANDLE hFind = FindFirstFile(wPath.c_str(), &FindFileData);
+    if (hFind == INVALID_HANDLE_VALUE) 
+    {
+        return false;
+    }
+    do
+    {
+        // Add to the list if this is not a directory
+        if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+        {
+            // Convert wchar to char
+            std::basic_string<TCHAR> wfilename(FindFileData.cFileName);
+            std::string filename;
+            filename.assign(wfilename.begin(), wfilename.end());
+            files.push_back(filename);
+        }
+    } while(FindNextFile(hFind, &FindFileData) != 0);
+
+    FindClose(hFind);
+    return true;
+#else
+    std::string path(FileSystem::getResourcePath());
+    if (dirPath && strlen(dirPath) > 0)
+	{
+		path.append(dirPath);
+	}
+    path.append("/.");
+    struct dirent* dp;
+    DIR* dir = opendir(path.c_str());
+    if (!dir)
+    {
+        return false;
+    }
+	while ((dp = readdir(dir)) != NULL)
+	{
+		std::string filepath(path);
+		filepath.append("/");
+		filepath.append(dp->d_name);
+
+		struct stat buf;
+		if (!stat(filepath.c_str(), &buf))
+		{
+            // Add to the list if this is not a directory
+			if (!S_ISDIR(buf.st_mode))
+			{
+				files.push_back(dp->d_name);
+			}
+		}
+	}
+	closedir(dir);
+    return true;
+#endif
+}
+
 FILE* FileSystem::openFile(const char* path, const char* mode)
 {
     std::string fullPath(__resourcePath);

+ 10 - 0
gameplay/src/FileSystem.h

@@ -33,6 +33,16 @@ public:
      */
     static const char* getResourcePath();
 
+    /**
+     * Lists the files in the specified directory and adds the files to the vector. Excludes directories.
+     * 
+     * @param dirPath Directory path relative to the path set in <code>setResourcePath(const char*)</code>.
+     * @param files The vector to append the files to.
+     * 
+     * @return True if successful, false if error.
+     */
+    static bool listFiles(const char* dirPath, std::vector<std::string>& files);
+
     /**
      * Opens the specified file.
      *

+ 9 - 0
gameplay/src/Game.cpp

@@ -250,4 +250,13 @@ void Game::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactI
 {
 }
 
+bool Game::mouseEvent(Mouse::MouseEvent evt, int x, int y)
+{
+    return false;
+}
+
+void Game::mouseWheelEvent(int x, int y, int delta)
+{
+}
+
 }

+ 24 - 0
gameplay/src/Game.h

@@ -3,6 +3,7 @@
 
 #include "Keyboard.h"
 #include "Touch.h"
+#include "Mouse.h"
 #include "AudioController.h"
 #include "AnimationController.h"
 #include "PhysicsController.h"
@@ -208,6 +209,29 @@ public:
      */
     virtual void touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    /**
+     * Mouse callback on mouse events. If the game does not consume the mouse move event or left mouse click event
+     * then it is interpreted as a touch event instead.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y);
+
+    /**
+     * Mouse callback on mouse wheel events.
+     *
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param delta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     */
+    virtual void mouseWheelEvent(int x, int y, int delta);
+
     /**
      * Sets muli-touch is to be enabled/disabled. Default is disabled.
      *

+ 145 - 18
gameplay/src/MaterialParameter.cpp

@@ -304,7 +304,7 @@ unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyI
                     return 0;
                 case FLOAT:
                 case INT:
-                    return 1;
+                    return _count;
                 case VECTOR2:
                     return 2 * _count;
                 case VECTOR3:
@@ -329,10 +329,30 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
             switch (_type)
             {
                 case FLOAT:
-                    value->setFloat(0, _value.floatValue);
+                    if (_count == 1)
+                    {
+                        value->setFloat(0, _value.floatValue);
+                    }
+                    else
+                    {
+                        for (unsigned int i = 0; i < _count; i++)
+                        {
+                            value->setFloat(i, _value.floatPtrValue[i]);
+                        }
+                    }
                     break;
                 case INT:
-                    value->setFloat(0, _value.intValue);
+                    if (_count == 1)
+                    {
+                        value->setFloat(0, _value.intValue);
+                    }
+                    else
+                    {
+                        for (unsigned int i = 0; i < _count; i++)
+                        {
+                            value->setFloat(i, _value.intPtrValue[i]);
+                        }
+                    }
                     break;
                 case VECTOR2:
                     for (unsigned int i = 0; i < _count; i++)
@@ -359,8 +379,12 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
     }
 }
 
-void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value)
+void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
+    assert(blendWeight >= 0.0f && blendWeight <= 1.0f);
+    if (blendWeight == 0.0f)
+        return;
+
     switch (propertyId)
     {
         case ANIMATE_UNIFORM:
@@ -368,29 +392,99 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
             switch (_type)
             {
                 case FLOAT:
-                    _value.floatValue = value->getFloat(0);
+                {
+                    if (_count == 1)
+                    {
+                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
+                        {
+                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+
+                            if (blendWeight != 1.0f)
+                                _value.floatValue = value->getFloat(0) * blendWeight;
+                            else
+                                _value.floatValue = value->getFloat(0);
+                        }
+                        else
+                        {
+                            if (blendWeight != 1.0f)
+                                _value.floatValue += value->getFloat(0) * blendWeight;
+                            else
+                                _value.floatValue += value->getFloat(0);
+                        }
+                    }
+                    else
+                    {
+                        applyAnimationValue(value, blendWeight, 1);
+                    }                    
                     break;
+                }
                 case INT:
-                    _value.intValue = value->getFloat(0);
-                    break;
-                case VECTOR2:
-                    for (unsigned int i = 0; i < _count; i++)
+                {
+                    if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
                     {
-                        value->getFloat(_value.floatPtrValue, i * 2, 2);
+                        _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+
+                        if (_count == 1)
+                        {
+                            if (blendWeight != 1.0f)
+                                _value.intValue = value->getFloat(0) * blendWeight;
+                            else
+                                _value.intValue = value->getFloat(0);
+                        }
+                        else
+                        {
+                            if (blendWeight != 1.0f)
+                            {
+                                for (unsigned int i = 0; i < _count; i++)
+                                    _value.intPtrValue[i] = value->getFloat(i) * blendWeight;
+                            }
+                            else
+                            {
+                                for (unsigned int i = 0; i < _count; i++)
+                                    _value.intPtrValue[i] = value->getFloat(i);
+                            }
+                        }
                     }
-                    break;
-                case VECTOR3:
-                    for (unsigned int i = 0; i < _count; i++)
+                    else
                     {
-                        value->getFloat(_value.floatPtrValue, i * 3, 3);
+                        if (_count == 1)
+                        {
+                            if (blendWeight != 1.0f)
+                                _value.intValue += value->getFloat(0) * blendWeight;
+                            else
+                                _value.intValue += value->getFloat(0);
+                        }
+                        else
+                        {
+                            if (blendWeight != 1.0f)
+                            {
+                                for (unsigned int i = 0; i < _count; i++)
+                                    _value.intPtrValue[i] += value->getFloat(i) * blendWeight;
+                            }
+                            else
+                            {
+                                for (unsigned int i = 0; i < _count; i++)
+                                    _value.intPtrValue[i] += value->getFloat(i);
+                            }
+                        }
                     }
                     break;
+                }
+                case VECTOR2:
+                {
+                    applyAnimationValue(value, blendWeight, 2);
+                    break;
+                }
+                case VECTOR3:
+                {
+                    applyAnimationValue(value, blendWeight, 3);
+                    break;
+                }
                 case VECTOR4:
-                    for (unsigned int i = 0; i < _count; i++)
-                    {
-                        value->getFloat(_value.floatPtrValue, i * 4, 4);
-                    }
+                {
+                    applyAnimationValue(value, blendWeight, 4);
                     break;
+                }
 
                 // UNSUPPORTED: NONE, MATRIX, METHOD, SAMPLER 
             }
@@ -398,4 +492,37 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
     }
 }
 
+void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWeight, int components)
+{
+    unsigned int count = _count * components;
+    if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+
+        if (blendWeight != 1.0f)
+        {
+            for (unsigned int i = 0; i < count; i++)
+                _value.floatPtrValue[i] = value->getFloat(i) * blendWeight;
+        }
+        else
+        {
+            for (unsigned int i = 0; i < count; i++)
+                _value.floatPtrValue[i] = value->getFloat(i);
+        }
+    }
+    else
+    {
+        if (blendWeight != 1.0f)
+        {
+            for (unsigned int i = 0; i < count; i++)
+                _value.floatPtrValue[i] += (value->getFloat(i) * blendWeight);
+        }
+        else
+        {
+            for (unsigned int i = 0; i < count; i++)
+                _value.floatPtrValue[i] += value->getFloat(i);
+        }
+    }
+}
+
 }

+ 5 - 1
gameplay/src/MaterialParameter.h

@@ -164,7 +164,7 @@ public:
     /**
      * @see AnimationTarget#setAnimationProperty
      */
-    void setAnimationPropertyValue(int propertyId, AnimationValue* value);
+    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 private:
 
@@ -253,6 +253,10 @@ private:
         METHOD
     } _type;
 
+    static const char ANIMATION_UNIFORM_BIT = 0x01;
+
+    void applyAnimationValue(AnimationValue* value, float blendWeight, int components);
+
     unsigned int _count;
     bool _dynamic;
     std::string _name;

+ 0 - 2
gameplay/src/Matrix.cpp

@@ -635,8 +635,6 @@ bool Matrix::invert(Matrix* dst) const
     inverse.m[14] = -m[12] * a3 + m[13] * a1 - m[14] * a0;
     inverse.m[15] = m[8] * a3 - m[9] * a1 + m[10] * a0;
 
-    memcpy(dst->m, inverse.m, MATRIX_SIZE);
-
     multiply(inverse, 1.0f / det, dst);
 
     return true;

+ 39 - 0
gameplay/src/Mouse.h

@@ -0,0 +1,39 @@
+#ifndef MOUSE_H_
+#define MOUSE_H_
+
+namespace gameplay
+{
+
+/**
+ * Mouse event
+ */
+class Mouse
+{
+public:
+
+    /**
+     * The mouse event type.
+     */
+    enum MouseEvent
+    {
+        MOUSE_LEFT_BUTTON_PRESS,
+        MOUSE_LEFT_BUTTON_RELEASE,
+        MOUSE_MIDDLE_BUTTON_PRESS,
+        MOUSE_MIDDLE_BUTTON_RELEASE,
+        MOUSE_RIGHT_BUTTON_PRESS,
+        MOUSE_RIGHT_BUTTON_RELEASE,
+        MOUSE_MOVE
+    };
+
+
+private:
+
+    /**
+     * Constructor. Used internally.
+     */
+    Mouse();
+};
+
+}
+
+#endif

+ 136 - 81
gameplay/src/PhysicsController.cpp

@@ -297,6 +297,23 @@ void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
             break;
         }
     }
+
+    // Find the rigid body's collision shape and release the rigid body's reference to it.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
+    {
+        if (_shapes[i]->_shape == rigidBody->_shape)
+        {
+            if (_shapes[i]->getRefCount() == 1)
+            {
+                _shapes[i]->release();
+                _shapes.erase(_shapes.begin() + i);
+            }
+            else
+                _shapes[i]->release();
+
+            return;
+        }
+    }
 }
 
 PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* collisionObject)
@@ -314,12 +331,51 @@ PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* colli
 btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const btVector3& scale)
 {
     btVector3 halfExtents(scale.x() * 0.5 * abs(max.x - min.x), scale.y() * 0.5 * abs(max.y - min.y), scale.z() * 0.5 * abs(max.z - min.z));
+
+    // Return the box shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
+    {
+        if (_shapes[i]->_shape->getShapeType() == BOX_SHAPE_PROXYTYPE)
+        {
+            btBoxShape* box = static_cast<btBoxShape*>(_shapes[i]->_shape);
+            if (box->getHalfExtentsWithMargin() == halfExtents)
+            {
+                _shapes[i]->addRef();
+                return box;
+            }
+        }
+    }
+    
+    // Create the box shape and add it to the cache.
     btBoxShape* box = bullet_new<btBoxShape>(halfExtents);
-    _shapes.push_back(box);
+    _shapes.push_back(new PhysicsCollisionShape(box));
 
     return box;
 }
 
+btCollisionShape* PhysicsController::createCapsule(float radius, float height)
+{
+    // Return the capsule shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
+    {
+        if (_shapes[i]->_shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE)
+        {
+            btCapsuleShape* capsule = static_cast<btCapsuleShape*>(_shapes[i]->_shape);
+            if (capsule->getRadius() == radius && capsule->getHalfHeight() == 0.5f * height)
+            {
+                _shapes[i]->addRef();
+                return capsule;
+            }
+        }
+    }
+    
+    // Create the capsule shape and add it to the cache.
+    btCapsuleShape* capsule = bullet_new<btCapsuleShape>(radius, height);
+    _shapes.push_back(new PhysicsCollisionShape(capsule));
+
+    return capsule;
+}
+
 btCollisionShape* PhysicsController::createSphere(float radius, const btVector3& scale)
 {
     // Since sphere shapes depend only on the radius, the best we can do is take
@@ -330,8 +386,23 @@ btCollisionShape* PhysicsController::createSphere(float radius, const btVector3&
     if (uniformScale < scale.z())
         uniformScale = scale.z();
     
+    // Return the sphere shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
+    {
+        if (_shapes[i]->_shape->getShapeType() == SPHERE_SHAPE_PROXYTYPE)
+        {
+            btSphereShape* sphere = static_cast<btSphereShape*>(_shapes[i]->_shape);
+            if (sphere->getRadius() == uniformScale * radius)
+            {
+                _shapes[i]->addRef();
+                return sphere;
+            }
+        }
+    }
+
+    // Create the sphere shape and add it to the cache.
     btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
-    _shapes.push_back(sphere);
+    _shapes.push_back(new PhysicsCollisionShape(sphere));
     
     return sphere;
 }
@@ -431,7 +502,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
     }
 
     btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
-    _shapes.push_back(shape);
+    _shapes.push_back(new PhysicsCollisionShape(shape));
 
     return shape;
 }
@@ -480,103 +551,87 @@ void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
     
 PhysicsController::DebugDrawer::DebugDrawer()
     : _mode(btIDebugDraw::DBG_DrawAabb | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints | 
-       btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _effect(NULL), _positionAttrib(0), _colorAttrib(0),
-       _viewProjectionMatrixUniform(NULL), _viewProjection(NULL), _vertexData(NULL), _vertexCount(0), _vertexDataSize(0)
+       btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _viewProjection(NULL), _meshBatch(NULL)
 {
-    // Unused
+    // Vertex shader for drawing colored lines.
+    const char* vs_str = 
+    {
+        "uniform mat4 u_viewProjectionMatrix;\n"
+        "attribute vec4 a_position;\n"
+        "attribute vec4 a_color;\n"
+        "varying vec4 v_color;\n"
+        "void main(void) {\n"
+        "    v_color = a_color;\n"
+        "    gl_Position = u_viewProjectionMatrix * a_position;\n"
+        "}"
+    };
+        
+    // Fragment shader for drawing colored lines.
+    const char* fs_str = 
+    {
+    #ifdef OPENGL_ES
+        "precision highp float;\n"
+    #endif
+        "varying vec4 v_color;\n"
+        "void main(void) {\n"
+        "   gl_FragColor = v_color;\n"
+        "}"
+    };
+        
+    Effect* effect = Effect::createFromSource(vs_str, fs_str);
+    Material* material = Material::create(effect);
+
+    VertexFormat::Element elements[] =
+    {
+        VertexFormat::Element(VertexFormat::POSITION, 3),
+        VertexFormat::Element(VertexFormat::COLOR, 4),
+    };
+    _meshBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false);
+    
+    SAFE_RELEASE(material);
+    SAFE_RELEASE(effect);
 }
 
 PhysicsController::DebugDrawer::~DebugDrawer()
 {
-    SAFE_RELEASE(_effect);
-    SAFE_DELETE_ARRAY(_vertexData);
+    SAFE_DELETE(_meshBatch);
 }
 
 void PhysicsController::DebugDrawer::begin(const Matrix& viewProjection)
 {
     _viewProjection = &viewProjection;
-    _vertexCount = 0;
+    _meshBatch->begin();
 }
 
 void PhysicsController::DebugDrawer::end()
 {
-    // Lazy load the effect for drawing.
-    if (!_effect)
-    {
-        // Vertex shader for drawing colored lines.
-        const char* vs_str = 
-        {
-            "uniform mat4 u_viewProjectionMatrix;\n"
-            "attribute vec4 a_position;\n"
-            "attribute vec4 a_color;\n"
-            "varying vec4 v_color;\n"
-            "void main(void) {\n"
-            "    v_color = a_color;\n"
-            "    gl_Position = u_viewProjectionMatrix * a_position;\n"
-            "}"
-        };
-        
-        // Fragment shader for drawing colored lines.
-        const char* fs_str = 
-        {
-        #ifdef OPENGL_ES
-            "precision highp float;\n"
-        #endif
-            "varying vec4 v_color;\n"
-            "void main(void) {\n"
-            "   gl_FragColor = v_color;\n"
-            "}"
-        };
-        
-        _effect = Effect::createFromSource(vs_str, fs_str);
-        _positionAttrib = _effect->getVertexAttribute("a_position");
-        _colorAttrib = _effect->getVertexAttribute("a_color");
-        _viewProjectionMatrixUniform = _effect->getUniform("u_viewProjectionMatrix");
-    }
-    
-    // Bind the effect and set the vertex attributes.
-    _effect->bind();
-    GL_ASSERT( glEnableVertexAttribArray(_positionAttrib) );
-    GL_ASSERT( glEnableVertexAttribArray(_colorAttrib) );
-    GL_ASSERT( glVertexAttribPointer(_positionAttrib, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 7, _vertexData) );
-    GL_ASSERT( glVertexAttribPointer(_colorAttrib, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 7, &_vertexData[3]) );
-    
-    // Set the camera's view projection matrix and draw.
-    _effect->setValue( _viewProjectionMatrixUniform, _viewProjection);
-    GL_ASSERT( glDrawArrays(GL_LINES, 0, _vertexCount / 7) );
+    _meshBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_viewProjection);
+    _meshBatch->draw();
+    _meshBatch->end();
 }
 
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
 {
-    // Allocate extra space in the vertex data batch if it is needed.
-    if (_vertexDataSize - _vertexCount < 14)
-    {
-        if (_vertexDataSize > 0)
-        {
-            unsigned int newVertexDataSize = _vertexDataSize * 2;
-            float* newVertexData = new float[newVertexDataSize];
-            memcpy(newVertexData, _vertexData, _vertexDataSize * sizeof(float));
-            SAFE_DELETE_ARRAY(_vertexData);
-            _vertexData = newVertexData;
-            _vertexDataSize = newVertexDataSize;
-        }
-        else
-        {
-            _vertexDataSize = INITIAL_CAPACITY;
-            _vertexData = new float[_vertexDataSize];
-        }
-    }
+    static DebugDrawer::DebugVertex fromVertex, toVertex;
     
-    // Create the vertex data for the line and copy it into the batch.
-    float vertexData[] = 
-    {
-        from.getX(), from.getY(), from.getZ(), 
-        fromColor.getX(), fromColor.getY(), fromColor.getZ(), 1.0f,
-        to.getX(), to.getY(), to.getZ(),
-        toColor.getX(), toColor.getY(), toColor.getZ(), 1.0f
-    };
-    memcpy(&_vertexData[_vertexCount], vertexData, sizeof(float) * 14);
-    _vertexCount += 14;
+    fromVertex.x = from.getX();
+    fromVertex.y = from.getY();
+    fromVertex.z = from.getZ();
+    fromVertex.r = fromColor.getX();
+    fromVertex.g = fromColor.getY();
+    fromVertex.b = fromColor.getZ();
+    fromVertex.a = 1.0f;
+
+    toVertex.x = to.getX();
+    toVertex.y = to.getY();
+    toVertex.z = to.getZ();
+    toVertex.r = toColor.getX();
+    toVertex.g = toColor.getY();
+    toVertex.b = toColor.getZ();
+    toVertex.a = 1.0f;
+
+    _meshBatch->add(&fromVertex, 1);
+    _meshBatch->add(&toVertex, 1);
 }
 
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)

+ 20 - 9
gameplay/src/PhysicsController.h

@@ -191,6 +191,14 @@ public:
 
 private:
 
+    struct PhysicsCollisionShape : public Ref
+    {
+        PhysicsCollisionShape(btCollisionShape* shape) : _shape(shape) {}
+        ~PhysicsCollisionShape() { SAFE_DELETE(_shape); }
+
+        btCollisionShape* _shape;
+    };
+
     /**
      * Constructor.
      */
@@ -238,6 +246,9 @@ private:
     // Creates a box collision shape to be used in the creation of a rigid body.
     btCollisionShape* createBox(const Vector3& min, const Vector3& max, const btVector3& scale);
 
+    // Creates a capsule collision shape to be used in the creation of a rigid body.
+    btCollisionShape* createCapsule(float radius, float height);
+
     // Creates a sphere collision shape to be used in the creation of a rigid body.
     btCollisionShape* createSphere(float radius, const btVector3& scale);
 
@@ -258,6 +269,12 @@ private:
     {
     public:
 
+        struct DebugVertex
+        {
+            float x, y, z;
+            float r, g, b, a;
+        };
+
         DebugDrawer();        
         ~DebugDrawer();
         
@@ -268,7 +285,7 @@ private:
         void drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor);        
         void drawLine(const btVector3& from, const btVector3& to, const btVector3& color);        
         void drawContactPoint(const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color);        
-        void reportErrorWarning(const char* warningString);        
+        void reportErrorWarning(const char* warningString);
         void draw3dText(const btVector3& location, const char* textString);        
         void setDebugMode(int mode);        
         int	getDebugMode() const;
@@ -276,14 +293,8 @@ private:
     private:
         
         int _mode;
-        Effect* _effect;
-        VertexAttribute _positionAttrib;
-        VertexAttribute _colorAttrib;
-        Uniform* _viewProjectionMatrixUniform;
         const Matrix* _viewProjection;
-        float* _vertexData;
-        unsigned int _vertexCount;
-        unsigned int _vertexDataSize;
+        MeshBatch* _meshBatch;
     };
     
     btDefaultCollisionConfiguration* _collisionConfiguration;
@@ -291,7 +302,7 @@ private:
     btBroadphaseInterface* _overlappingPairCache;
     btSequentialImpulseConstraintSolver* _solver;
     btDynamicsWorld* _world;
-    btAlignedObjectArray<btCollisionShape*> _shapes;
+    std::vector<PhysicsCollisionShape*> _shapes;
     DebugDrawer* _debugDrawer;
     Listener::EventType _status;
     std::vector<Listener*>* _listeners;

+ 79 - 27
gameplay/src/PhysicsRigidBody.cpp

@@ -14,15 +14,16 @@ const int PhysicsRigidBody::Listener::DIRTY         = 0x01;
 const int PhysicsRigidBody::Listener::COLLISION     = 0x02;
 const int PhysicsRigidBody::Listener::REGISTERED    = 0x04;
 
-// Internal values used for creating mesh and heightfield rigid bodies.
+// Internal values used for creating mesh, heightfield, and capsule rigid bodies.
 #define SHAPE_MESH ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 1))
 #define SHAPE_HEIGHTFIELD ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 2))
+#define SHAPE_CAPSULE ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 3))
 
 // Helper function for calculating heights from heightmap (image) or heightfield data.
 static float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y);
 
 PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass, 
-        float friction, float restitution, float linearDamping, float angularDamping)
+    float friction, float restitution, float linearDamping, float angularDamping)
         : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
         _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
         _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
@@ -144,6 +145,29 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, Image* image, float mass,
     _node->addListener(this);
 }
 
+PhysicsRigidBody::PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction,
+    float restitution, float linearDamping, float angularDamping)
+        : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
+        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
+        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
+{
+    // Create the capsule collision shape.
+    _shape = Game::getInstance()->getPhysicsController()->createCapsule(radius, height);
+
+    // Use the center of the bounding sphere as the center of mass offset.
+    Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
+    c.negate();
+
+    // Create the Bullet rigid body.
+    if (c.lengthSquared() > MATH_EPSILON)
+        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
+    else
+        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
+
+    // Add the rigid body to the physics world.
+    Game::getInstance()->getPhysicsController()->addRigidBody(this);
+}
+
 PhysicsRigidBody::~PhysicsRigidBody()
 {
     // Clean up all constraints linked to this rigid body.
@@ -161,8 +185,6 @@ PhysicsRigidBody::~PhysicsRigidBody()
         if (_body->getMotionState())
             delete _body->getMotionState();
 
-        SAFE_DELETE(_shape);
-
         Game::getInstance()->getPhysicsController()->removeRigidBody(this);
         SAFE_DELETE(_body);
     }
@@ -294,6 +316,8 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     Vector3* gravity = NULL;
     Vector3* anisotropicFriction = NULL;
     const char* imagePath = NULL;
+    float radius = -1.0f;
+    float height = -1.0f;
 
     // Load the defined properties.
     properties->rewind();
@@ -311,6 +335,8 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
                 type = SHAPE_MESH;
             else if (typeStr == "HEIGHTFIELD")
                 type = SHAPE_HEIGHTFIELD;
+            else if (typeStr == "CAPSULE")
+                type = SHAPE_CAPSULE;
             else
             {
                 WARN_VARG("Could not create rigid body; unsupported value for rigid body type: '%s'.", typeStr.c_str());
@@ -355,6 +381,14 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         {
             imagePath = properties->getString();
         }
+        else if (strcmp(name, "radius") == 0)
+        {
+            radius = properties->getFloat();
+        }
+        else if (strcmp(name, "height") == 0)
+        {
+            height = properties->getFloat();
+        }
     }
 
     // If the rigid body type is equal to mesh, check that the node's mesh's primitive type is supported.
@@ -380,30 +414,48 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 
     // Create the rigid body.
     PhysicsRigidBody* body = NULL;
-    if (imagePath == NULL)
-    {
-        body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
-    }
-    else
+    switch (type)
     {
-        // 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;
-        }
-
-        body = new PhysicsRigidBody(node, image, mass, friction, restitution, linearDamping, angularDamping);
-        SAFE_RELEASE(image);
+        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;
+                }
+
+                body = new PhysicsRigidBody(node, image, mass, friction, restitution, linearDamping, angularDamping);
+                SAFE_RELEASE(image);
+            }
+            break;
+        case SHAPE_CAPSULE:
+            if (radius == -1.0f || height == -1.0f)
+            {
+                WARN("Both 'radius' and 'height' must be specified for a capsule rigid body.");
+            }
+            else
+            {
+                body = new PhysicsRigidBody(node, radius, height, mass, friction, restitution, linearDamping, angularDamping);
+            }
+            break;
+        default:
+            body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
+            break;
     }
 
     // Set any initially defined properties.

+ 17 - 0
gameplay/src/PhysicsRigidBody.h

@@ -325,6 +325,23 @@ private:
     PhysicsRigidBody(Node* node, Image* image, float mass, float friction = 0.5,
         float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
 
+    /**
+     * Creates a capsule rigid body.
+     * 
+     * @param node The node to create the heightfield rigid body for; note that the node must have
+     *      a model attached to it prior to creating a rigid body for it.
+     * @param radius The radius of the capsule.
+     * @param height The height of the cylindrical part of the capsule (not including the ends).
+     * @param mass The mass of the rigid body, in kilograms.
+     * @param friction The friction of the rigid body (non-zero values give best simulation results).
+     * @param restitution The restitution of the rigid body (this controls the bounciness of
+     *      the rigid body; use zero for best simulation results).
+     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
+     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
+     */
+    PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction = 0.5,
+        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
+
     /**
      * Destructor.
      */

+ 105 - 2
gameplay/src/PlatformQNX.cpp

@@ -665,6 +665,23 @@ long timespec2millis(struct timespec *a)
     return a->tv_sec*1000 + a->tv_nsec/1000000;
 }
 
+/**
+ * Fires a mouse event or a touch event on the game.
+ * If the mouse event is not consumed, a touch event is fired instead.
+ *
+ * @param mouseEvent The mouse event to fire.
+ * @param touchEvent The touch event to fire.
+ * @param x The x position of the touch in pixels.
+ * @param y The y position of the touch in pixels.
+ */
+void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
+{
+    if (!Game::getInstance()->mouseEvent(mouseEvent, x, y))
+    {
+        Game::getInstance()->touchEvent(touchEvent, x, y, 0);
+    }
+}
+
 int Platform::enterMessagePump()
 {
     int rc;
@@ -710,7 +727,7 @@ int Platform::enterMessagePump()
                         if (!__multiTouch)
                         {
                             screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_POSITION, position);
-                           Game::getInstance()->touchEvent(Touch::TOUCH_PRESS, position[0], position[1], 0);
+                            Game::getInstance()->touchEvent(Touch::TOUCH_PRESS, position[0], position[1], 0);
                         }
                         else
                         {
@@ -725,7 +742,7 @@ int Platform::enterMessagePump()
                         if (!__multiTouch)
                         {
                             screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_POSITION, position);
-                           Game::getInstance()->touchEvent(Touch::TOUCH_RELEASE, position[0], position[1], 0);
+                            Game::getInstance()->touchEvent(Touch::TOUCH_RELEASE, position[0], position[1], 0);
                         }
                         else
                         {
@@ -751,6 +768,92 @@ int Platform::enterMessagePump()
                         break;
                     }
 
+                    case SCREEN_EVENT_POINTER:
+                    {
+                        static int mouse_pressed = 0;
+                        int buttons;
+                        int wheel;
+                        // A move event will be fired unless a button state changed.
+                        bool move = true;
+                        bool left_move = false;
+                        //This is a mouse move event, it is applicable to a device with a usb mouse or simulator
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
+
+                        // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
+                        if (buttons & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                            {
+                                left_move = true;
+                            }
+                            else
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
+                                mouseOrTouchEvent(Mouse::MOUSE_LEFT_BUTTON_PRESS, Touch::TOUCH_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
+                            mouseOrTouchEvent(Mouse::MOUSE_LEFT_BUTTON_RELEASE, Touch::TOUCH_RELEASE, position[0], position[1]);
+                        }
+
+                        // Handle right mouse
+                        if (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
+                                Game::getInstance()->mouseEvent(Mouse::MOUSE_RIGHT_BUTTON_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
+                            Game::getInstance()->mouseEvent(Mouse::MOUSE_RIGHT_BUTTON_RELEASE, position[0], position[1]);
+                        }
+
+                        // Handle middle mouse
+                        if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
+                                Game::getInstance()->mouseEvent(Mouse::MOUSE_MIDDLE_BUTTON_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
+                            Game::getInstance()->mouseEvent(Mouse::MOUSE_MIDDLE_BUTTON_RELEASE, position[0], position[1]);
+                        }
+
+                        // Fire a move event if none of the buttons changed.
+                        if (left_move)
+                        {
+                            mouseOrTouchEvent(Mouse::MOUSE_MOVE, Touch::TOUCH_MOVE, position[0], position[1]);
+                        }
+                        else if (move)
+                        {
+                            Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, position[0], position[1]);
+                        }
+
+                        // Handle mouse wheel events
+                        if (wheel)
+                        {
+                            Game::getInstance()->mouseWheelEvent(position[0], position[1], -wheel);
+                        }
+                        break;
+                    }
+
                     case SCREEN_EVENT_KEYBOARD:
                     {
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);

+ 27 - 3
gameplay/src/PlatformWin32.cpp

@@ -267,26 +267,43 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         return 0;
 
     case WM_LBUTTONDOWN:
-        gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_PRESS, LOWORD(lParam), HIWORD(lParam), 0);
+        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_LEFT_BUTTON_PRESS, LOWORD(lParam), HIWORD(lParam)))
+        {
+            gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_PRESS, LOWORD(lParam), HIWORD(lParam), 0);
+        }
         lMouseDown = true;
         return 0;
 
     case WM_LBUTTONUP:
         lMouseDown = false;
-        gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_RELEASE, LOWORD(lParam), HIWORD(lParam), 0);
+        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_LEFT_BUTTON_RELEASE, LOWORD(lParam), HIWORD(lParam)))
+        {
+            gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_RELEASE, LOWORD(lParam), HIWORD(lParam), 0);
+        }
         return 0;
 
     case WM_RBUTTONDOWN:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RIGHT_BUTTON_PRESS, LOWORD(lParam), HIWORD(lParam));
         rMouseDown = true;
         lx = LOWORD(lParam);
         ly = HIWORD(lParam);
         break;
 
     case WM_RBUTTONUP:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RIGHT_BUTTON_RELEASE, LOWORD(lParam), HIWORD(lParam));
         rMouseDown = false;
         break;
 
+    case WM_MBUTTONDOWN:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MIDDLE_BUTTON_PRESS, LOWORD(lParam), HIWORD(lParam));
+        break;
+
+    case WM_MBUTTONUP:
+        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MIDDLE_BUTTON_RELEASE, LOWORD(lParam), HIWORD(lParam));
+        break;
+
     case WM_MOUSEMOVE:
+    {
         if (!hasMouse)
         {
             hasMouse = true;
@@ -299,8 +316,10 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
             TrackMouseEvent(&tme);
         }
 
-        if (lMouseDown)
+        bool consumed = gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, LOWORD(lParam), HIWORD(lParam));
+        if (lMouseDown && !consumed)
         {
+            // Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
             gameplay::Game::getInstance()->touchEvent(gameplay::Touch::TOUCH_MOVE, LOWORD(lParam), HIWORD(lParam), 0);
             return 0;
         }
@@ -319,6 +338,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
             ly = HIWORD(lParam);
         }
         break;
+    }
 
     case WM_MOUSELEAVE:
         hasMouse = false;
@@ -326,6 +346,10 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         rMouseDown = false;
         break;
 
+    case WM_MOUSEWHEEL:
+        gameplay::Game::getInstance()->mouseWheelEvent(LOWORD(lParam), HIWORD(lParam), GET_WHEEL_DELTA_WPARAM(wParam) / 120);
+        break;
+
     case WM_KEYDOWN:
         if (wParam == VK_LSHIFT || wParam == VK_RSHIFT)
             shiftDown = true;

+ 42 - 28
gameplay/src/Quaternion.cpp

@@ -262,28 +262,55 @@ void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quate
 }
 
 void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
+{
+    slerp(q1.x, q1.y, q1.z, q1.w, q2.x, q2.y, q2.z, q2.w, t, &dst->x, &dst->y, &dst->z, &dst->w);
+}
+
+void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
+{
+    assert(dst);
+    assert(!(t < 0.0f || t > 1.0f));
+
+    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
+    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
+
+    slerpForSquad(q1, q2, t, &dstQ);
+    slerpForSquad(s1, s2, t, &dstS);
+    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+}
+
+void Quaternion::slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw)
 {
     // Fast slerp implementation by kwhatmough:
     // It contains no division operations, no trig, no inverse trig
     // and no sqrt. Not only does this code tolerate small constraint
     // errors in the input quaternions, it actually corrects for them.
-    assert(dst);
+    assert(dstx && dsty && dstz && dstw);
     assert(!(t < 0.0f || t > 1.0f));
 
     if (t == 0.0f)
     {
-        memcpy(dst, &q1, sizeof(float) * 4);
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
         return;
     }
     else if (t == 1.0f)
     {
-        memcpy(dst, &q2, sizeof(float) * 4);
+        *dstx = q2x;
+        *dsty = q2y;
+        *dstz = q2z;
+        *dstw = q2w;
         return;
     }
 
-    if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w)
+    if (q1x == q2x && q1y == q2y && q1z == q2z && q1w == q2w)
     {
-        memcpy(dst, &q1, sizeof(float) * 4);
+        *dstx = q1x;
+        *dsty = q1y;
+        *dstz = q1z;
+        *dstw = q1w;
         return;
     }
 
@@ -293,7 +320,7 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
     float halfSecHalfTheta, versHalfTheta;
     float sqNotU, sqU;
 
-    float cosTheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+    float cosTheta = q1w * q2w + q1x * q2x + q1y * q2y + q1z * q2z;
 
     // As usual in all slerp implementations, we fold theta.
     alpha = cosTheta >= 0 ? 1.0f : -1.0f;
@@ -334,34 +361,21 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
     beta = f1 + f2b;
 
     // Apply final coefficients to a and b as usual.
-    float w = alpha * q1.w + beta * q2.w;
-    float x = alpha * q1.x + beta * q2.x;
-    float y = alpha * q1.y + beta * q2.y;
-    float z = alpha * q1.z + beta * q2.z;
+    float w = alpha * q1w + beta * q2w;
+    float x = alpha * q1x + beta * q2x;
+    float y = alpha * q1y + beta * q2y;
+    float z = alpha * q1z + beta * q2z;
 
     // This final adjustment to the quaternion's length corrects for
-    // any small constraint error in the inputs q1 and q2. But as you
+    // any small constraint error in the inputs q1 and q2 But as you
     // can see, it comes at the cost of 9 additional multiplication
     // operations. If this error-correcting feature is not required,
     // the following code may be removed.
     f1 = 1.5f - 0.5f * (w * w + x * x + y * y + z * z);
-    dst->w = w * f1;
-    dst->x = x * f1;
-    dst->y = y * f1;
-    dst->z = z * f1;
-}
-
-void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
-{
-    assert(dst);
-    assert(!(t < 0.0f || t > 1.0f));
-
-    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
-    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
-
-    slerpForSquad(q1, q2, t, &dstQ);
-    slerpForSquad(s1, s2, t, &dstS);
-    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+    *dstw = w * f1;
+    *dstx = x * f1;
+    *dsty = y * f1;
+    *dstz = z * f1;
 }
 
 void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)

+ 30 - 2
gameplay/src/Quaternion.h

@@ -40,6 +40,8 @@ class Matrix;
  */
 class Quaternion
 {
+    friend class Curve;
+
 public:
 
     /**
@@ -293,7 +295,7 @@ public:
      * @param dst A quaternion to store the result in.
      */
     static void lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates between two quaternions using spherical linear interpolation.
      *
@@ -310,7 +312,7 @@ public:
      * @param dst A quaternion to store the result in.
      */
     static void slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
-
+    
     /**
      * Interpolates over a series of quaternions using spherical spline interpolation.
      *
@@ -350,6 +352,32 @@ public:
 
 private:
 
+    /**
+     * Interpolates between two quaternions using spherical linear interpolation.
+     *
+     * Spherical linear interpolation provides smooth transitions between different
+     * orientations and is often useful for animating models or cameras in 3D.
+     *
+     * Note: For accurate interpolation, the input quaternions must be at (or close to) unit length.
+     * This method does not automatically normalize the input quaternions, so it is up to the
+     * caller to ensure they call normalize beforehand, if necessary.
+     *
+     * @param q1x The x component of the first quaternion.
+     * @param q1y The y component of the first quaternion.
+     * @param q1z The z component of the first quaternion.
+     * @param q1w The w component of the first quaternion.
+     * @param q2x The x component of the second quaternion.
+     * @param q2y The y component of the second quaternion.
+     * @param q2z The z component of the second quaternion.
+     * @param q2w The w component of the second quaternion.
+     * @param t The interpolation coefficient.
+     * @param dstx A pointer to store the x component of the slerp in.
+     * @param dsty A pointer to store the y component of the slerp in.
+     * @param dstz A pointer to store the z component of the slerp in.
+     * @param dstw A pointer to store the w component of the slerp in.
+     */
+    static void slerp(float q1x, float q1y, float q1z, float q1w, float q2x, float q2y, float q2z, float q2w, float t, float* dstx, float* dsty, float* dstz, float* dstw);
+
     static void slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
 };
 

+ 267 - 15
gameplay/src/Transform.cpp

@@ -226,6 +226,13 @@ void Transform::getRightVector(Vector3* dst) const
     getMatrix().getRightVector(dst);
 }
 
+void Transform::rotate(float qx, float qy, float qz, float qw)
+{
+    Quaternion q(qx, qy, qz, qw);
+    _rotation.multiply(q);
+    dirty();
+}
+
 void Transform::rotate(const Quaternion& rotation)
 {
     _rotation.multiply(rotation);
@@ -551,6 +558,7 @@ unsigned int Transform::getAnimationPropertyComponentCount(int propertyId) const
 {
     switch (propertyId)
     {
+        case ANIMATE_SCALE_UNIT:
         case ANIMATE_SCALE_X:
         case ANIMATE_SCALE_Y:
         case ANIMATE_SCALE_Z:
@@ -576,6 +584,9 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 {
     switch (propertyId)
     {
+        case ANIMATE_SCALE_UNIT:
+            value->setFloat(0, _scale.x);
+            break;
         case ANIMATE_SCALE:
             value->setFloat(0, _scale.x);
             value->setFloat(1, _scale.y);
@@ -636,46 +647,194 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
     }
 }
 
-void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value)
+void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
+    assert(blendWeight >= 0.0f && blendWeight <= 1.0f);
+    if (blendWeight == 0.0f)
+        return;
+
     switch (propertyId)
     {
+        case ANIMATE_SCALE_UNIT:
+        {
+            float scale = value->getFloat(0);
+
+            if (blendWeight != 1.0f)
+                scale *= blendWeight;
+
+            applyAnimationValueScaleX(scale);
+            applyAnimationValueScaleY(scale);
+            applyAnimationValueScaleZ(scale);
+            
+            break;
+        }   
         case ANIMATE_SCALE:
-            setScale(value->getFloat(0), value->getFloat(1), value->getFloat(2));
+        {
+            float sx = value->getFloat(0);
+            float sy = value->getFloat(1);
+            float sz = value->getFloat(2);
+            if (blendWeight != 1.0f)
+            {
+                sx *= blendWeight;
+                sy *= blendWeight;
+                sz *= blendWeight;
+            }
+
+            applyAnimationValueScaleX(sx);
+            applyAnimationValueScaleY(sy);
+            applyAnimationValueScaleZ(sz);
+
             break;
+        }
         case ANIMATE_SCALE_X:
-            setScaleX(value->getFloat(0));
+        {
+            float sx = value->getFloat(0);
+
+            if (blendWeight != 1.0f)
+                sx *= blendWeight;
+
+            applyAnimationValueScaleX(sx);
+
             break;
+        }
         case ANIMATE_SCALE_Y:
-            setScaleY(value->getFloat(0));
+        {
+            float sy = value->getFloat(0);
+
+            if (blendWeight != 1.0f)
+                sy *= blendWeight;
+
+            applyAnimationValueScaleY(sy);
+
             break;
+        }
         case ANIMATE_SCALE_Z:
-            setScaleZ(value->getFloat(0));
+        {
+            float sz = value->getFloat(0);
+
+            if (blendWeight != 1.0f)
+                sz *= blendWeight;
+
+            applyAnimationValueScaleZ(sz);
+
             break;
+        }
         case ANIMATE_ROTATE:
-            setRotation(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
+        {
+            Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
+
+            if (blendWeight != 1.0f)
+                Quaternion::slerp(Quaternion::identity(), q, blendWeight, &q);
+
+            applyAnimationValueRotation(&q);
+            
             break;
+        }
         case ANIMATE_TRANSLATE:
-            setTranslation(value->getFloat(0), value->getFloat(1), value->getFloat(2));
+        {
+            float tx = value->getFloat(0);
+            float ty = value->getFloat(1);
+            float tz = value->getFloat(2);
+
+            if (blendWeight != 1.0f)
+            {
+                tx *= blendWeight;
+                ty *= blendWeight;
+                tz *= blendWeight;
+            }
+
+            applyAnimationValueTranslationX(tx);
+            applyAnimationValueTranslationY(ty);
+            applyAnimationValueTranslationZ(tz);
+            
             break;
+        }
         case ANIMATE_TRANSLATE_X:
-            setTranslationX(value->getFloat(0));
+        {
+            float tx = value->getFloat(0);
+
+            if (blendWeight != 1.0f)
+                tx *= blendWeight;
+
+            applyAnimationValueTranslationX(tx);
+
             break;
+        }
         case ANIMATE_TRANSLATE_Y:
-            setTranslationY(value->getFloat(0));
+        {
+            float ty = value->getFloat(0);
+
+            if (blendWeight != 1.0f)
+                ty *= blendWeight;
+            
+            applyAnimationValueTranslationY(ty);
+
             break;
+        }
         case ANIMATE_TRANSLATE_Z:
-            setTranslationZ(value->getFloat(0));
+        {
+            float tz = value->getFloat(0);
+
+            if (blendWeight != 1.0f)
+                tz *= blendWeight;
+
+            applyAnimationValueTranslationZ(tz);
+
             break;
+        }
         case ANIMATE_ROTATE_TRANSLATE:
-            setRotation(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
-            setTranslation(value->getFloat(4), value->getFloat(5), value->getFloat(6));
+        {
+            Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
+            float tx = value->getFloat(4);
+            float ty = value->getFloat(5);
+            float tz = value->getFloat(6);
+
+            if (blendWeight != 1.0f)
+            {
+                Quaternion::slerp(Quaternion::identity(), q, blendWeight, &q);
+                tx *= blendWeight;
+                ty *= blendWeight;
+                tz *= blendWeight;
+            }
+
+            applyAnimationValueRotation(&q);
+            applyAnimationValueTranslationX(tx);
+            applyAnimationValueTranslationY(ty);
+            applyAnimationValueTranslationZ(tz);
+            
             break;
+        }
         case ANIMATE_SCALE_ROTATE_TRANSLATE:
-            setScale(value->getFloat(0), value->getFloat(1), value->getFloat(2));
-            setRotation(value->getFloat(3), value->getFloat(4), value->getFloat(5), value->getFloat(6));
-            setTranslation(value->getFloat(7), value->getFloat(8), value->getFloat(9));
+        {
+            float sx = value->getFloat(0);
+            float sy = value->getFloat(1);
+            float sz = value->getFloat(2);
+            Quaternion q(value->getFloat(3), value->getFloat(4), value->getFloat(5), value->getFloat(6));
+            float tx = value->getFloat(7);
+            float ty = value->getFloat(8);
+            float tz = value->getFloat(9);
+
+            if (blendWeight != 1.0f)
+            {
+                sx *= blendWeight;
+                sy *= blendWeight;
+                sz *= blendWeight;
+                Quaternion::slerp(Quaternion::identity(), q, blendWeight, &q);
+                tx *= blendWeight;
+                ty *= blendWeight;
+                tz *= blendWeight;
+            }
+
+            applyAnimationValueScaleX(sx);
+            applyAnimationValueScaleY(sy);
+            applyAnimationValueScaleZ(sz);
+            applyAnimationValueRotation(&q);
+            applyAnimationValueTranslationX(tx);
+            applyAnimationValueTranslationY(ty);
+            applyAnimationValueTranslationZ(tz);
+            
             break;
+        }
         default:
             break;
     }
@@ -725,4 +884,97 @@ void Transform::transformChanged()
     }
 }
 
+void Transform::applyAnimationValueScaleX(float sx)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_SCALE_X_BIT) != ANIMATION_SCALE_X_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_SCALE_X_BIT;
+        setScaleX(sx);
+    }
+    else
+    {
+        _scale.x += sx;
+        dirty();
+    }
+}
+
+void Transform::applyAnimationValueScaleY(float sy)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_SCALE_Y_BIT) != ANIMATION_SCALE_Y_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_SCALE_Y_BIT;
+        setScaleY(sy);
+    }
+    else
+    {
+        _scale.y += sy;
+        dirty();
+    }
+}
+
+void Transform::applyAnimationValueScaleZ(float sz)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_SCALE_Z_BIT) != ANIMATION_SCALE_Z_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_SCALE_Z_BIT;
+        setScaleZ(sz);
+    }
+    else
+    {
+        _scale.z += sz;
+        dirty();
+    }
+}
+
+void Transform::applyAnimationValueRotation(Quaternion* q)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_ROTATION_BIT) != ANIMATION_ROTATION_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_ROTATION_BIT;
+        setRotation(*q);
+    }
+    else
+    {
+        rotate(*q);
+    }
+}
+
+void Transform::applyAnimationValueTranslationX(float tx)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_X_BIT) != ANIMATION_TRANSLATION_X_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_X_BIT;
+        setTranslationX(tx);
+    }
+    else
+    {
+        translateX(tx);
+    }
+}
+
+void Transform::applyAnimationValueTranslationY(float ty)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Y_BIT) != ANIMATION_TRANSLATION_Y_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Y_BIT;
+        setTranslationY(ty);
+    }
+    else
+    {
+        translateY(ty);
+    }
+}
+
+void Transform::applyAnimationValueTranslationZ(float tz)
+{
+    if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Z_BIT) != ANIMATION_TRANSLATION_Z_BIT)
+    {
+        _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Z_BIT;
+        setTranslationZ(tz);
+    }
+    else
+    {
+        translateZ(tz);
+    }
+}
 }

+ 32 - 1
gameplay/src/Transform.h

@@ -30,6 +30,11 @@ class Transform : public AnimationTarget
 {
 public:
 
+    /**
+     * Scale animation property. Data=scale
+     */
+    static const int ANIMATE_SCALE_UNIT = 0;
+
     /**
      * Scale animation property. Data=sx,sy,sz
      */
@@ -319,6 +324,16 @@ public:
      */
     void getRightVector(Vector3* dst) const;
 
+    /**
+     * Rotates this transform's rotation component by the given rotation.
+     *
+     * @param qx The quaternion x value.
+     * @param qy The quaternion y value.
+     * @param qz The quaternion z value.
+     * @param qw The quaternion w value.
+     */
+    void rotate(float qx, float qy, float qz, float qw);
+
     /**
      * Rotates this transform's rotation component by the given rotation.
      *
@@ -710,7 +725,7 @@ public:
     /**
      * @see AnimationTarget#setAnimationProperty
      */
-    void setAnimationPropertyValue(int propertyId, AnimationValue* value);
+    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 protected:
 
@@ -730,6 +745,22 @@ protected:
     mutable bool _matrixDirty;
     std::list<TransformListener>* _listeners;
 
+private:
+    static const char ANIMATION_SCALE_X_BIT = 0x01; 
+    static const char ANIMATION_SCALE_Y_BIT = 0x02; 
+    static const char ANIMATION_SCALE_Z_BIT = 0x04; 
+    static const char ANIMATION_ROTATION_BIT = 0x08;  
+    static const char ANIMATION_TRANSLATION_X_BIT = 0x10; 
+    static const char ANIMATION_TRANSLATION_Y_BIT = 0x20; 
+    static const char ANIMATION_TRANSLATION_Z_BIT = 0x40; 
+
+    void applyAnimationValueScaleX(float sx);
+    void applyAnimationValueScaleY(float sy);
+    void applyAnimationValueScaleZ(float sz);
+    void applyAnimationValueRotation(Quaternion* q);
+    void applyAnimationValueTranslationX(float tx);
+    void applyAnimationValueTranslationY(float ty);
+    void applyAnimationValueTranslationZ(float tz);
 };
 
 }

+ 1 - 0
gameplay/src/gameplay.h

@@ -4,6 +4,7 @@
 #include "Game.h"
 #include "Keyboard.h"
 #include "Touch.h"
+#include "Mouse.h"
 #include "FileSystem.h"
 #include "Package.h"