2
0
Эх сурвалжийг харах

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

Darryl Gough 14 жил өмнө
parent
commit
bde704b2d5

+ 9 - 156
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,146 +295,16 @@ 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.
@@ -485,23 +355,6 @@ 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;
 }

+ 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.

+ 12 - 1
gameplay/src/AnimationController.cpp

@@ -333,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);
@@ -347,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;

+ 2 - 1
gameplay/src/AnimationController.h

@@ -205,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 - 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

+ 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;

+ 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);
 };
 

+ 261 - 16
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);
@@ -640,49 +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:
-            setScale(value->getFloat(0));
+        {
+            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;
     }
@@ -732,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);
+    }
+}
 }

+ 29 - 3
gameplay/src/Transform.h

@@ -31,12 +31,12 @@ class Transform : public AnimationTarget
 public:
 
     /**
-     * Scale animation property. Data=sx,sy,sz
+     * Scale animation property. Data=scale
      */
     static const int ANIMATE_SCALE_UNIT = 0;
 
     /**
-     * Scale animation property. Data=scale
+     * Scale animation property. Data=sx,sy,sz
      */
     static const int ANIMATE_SCALE = 1;
 
@@ -324,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.
      *
@@ -715,7 +725,7 @@ public:
     /**
      * @see AnimationTarget#setAnimationProperty
      */
-    void setAnimationPropertyValue(int propertyId, AnimationValue* value);
+    void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 protected:
 
@@ -735,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);
 };
 
 }