Ver Fonte

Merge pull request #98 from blackberry-gaming/next-kcunney

Interpolation optimization
Sean Paul Taylor há 14 anos atrás
pai
commit
4612fce5f9

+ 1 - 1
gameplay-encoder/src/MeshSkin.cpp

@@ -292,7 +292,7 @@ void MeshSkin::computeBounds()
         {
         case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
             curve = new Curve(keyCount, 10);
-            curve->addQuaternionOffset(3);
+            curve->setQuaternionOffset(3);
             break;
         }
         if (curve == NULL)

+ 18 - 24
gameplay/src/Animation.cpp

@@ -240,18 +240,7 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
 
     Curve* curve = new Curve(keyCount, propertyComponentCount);
     if (target->_targetType == AnimationTarget::TRANSFORM)
-    {
-        switch (propertyId)
-        {
-        case Transform::ANIMATE_ROTATE:
-        case Transform::ANIMATE_ROTATE_TRANSLATE:
-            curve->addQuaternionOffset(ANIMATION_ROTATE_OFFSET);
-            break;
-        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
-            curve->addQuaternionOffset(ANIMATION_SRT_OFFSET);
-            break;
-        }
-    }
+        setTransformRotationOffset(curve, propertyId);
 
     unsigned long lowest = keyTimes[0];
     unsigned long duration = keyTimes[keyCount-1] - lowest;
@@ -287,18 +276,7 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
 
     Curve* curve = new Curve(keyCount, propertyComponentCount);
     if (target->_targetType == AnimationTarget::TRANSFORM)
-    {
-        switch (propertyId)
-        {
-        case Transform::ANIMATE_ROTATE:
-        case Transform::ANIMATE_ROTATE_TRANSLATE:
-            curve->addQuaternionOffset(ANIMATION_ROTATE_OFFSET);
-            break;
-        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
-            curve->addQuaternionOffset(ANIMATION_SRT_OFFSET);
-            break;
-        }
-    }
+        setTransformRotationOffset(curve, propertyId);
     
     unsigned long lowest = keyTimes[0];
     unsigned long duration = keyTimes[keyCount-1] - lowest;
@@ -356,4 +334,20 @@ void Animation::removeChannel(Channel* channel)
         _controller->destroyAnimation(this);
 }
 
+void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId)
+{
+    switch (propertyId)
+    {
+    case Transform::ANIMATE_ROTATE:
+    case Transform::ANIMATE_ROTATE_TRANSLATE:
+        curve->setQuaternionOffset(ANIMATION_ROTATE_OFFSET);
+        return;
+    case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
+        curve->setQuaternionOffset(ANIMATION_SRT_OFFSET);
+        return;
+    }
+
+    return;
+}
+
 }

+ 5 - 0
gameplay/src/Animation.h

@@ -173,6 +173,11 @@ private:
      * Removes a channel from the animation.
      */
     void removeChannel(Channel* channel);
+
+    /**
+     * Sets the rotation offset in a Curve representing a Transform's animation data.
+     */
+    void setTransformRotationOffset(Curve* curve, unsigned int propertyId);
     
     AnimationController* _controller;       // The AnimationController that this Animation will run on.
     std::string _id;                        // The Animation's ID.

+ 47 - 81
gameplay/src/AnimationClip.cpp

@@ -311,7 +311,7 @@ bool AnimationClip::update(unsigned long elapsedTime)
             // Evaluate point on Curve.
             channel->_curve->evaluate(percentComplete, value->_interpolatedValue);
 
-            if (channel->_curve->_quaternionOffsetsCount == 0)
+            if (!channel->_curve->_quaternionOffset)
             {
                 if (value->_isFirstActing)
                 {
@@ -338,131 +338,97 @@ bool AnimationClip::update(unsigned long elapsedTime)
             }
             else
             {   //We have Quaternions!!!
-                unsigned int j = 0;
-                unsigned int quaternionOffsetIndex = 0;
-                unsigned int quaternionOffset = 0;
-
+                unsigned int quaternionOffset = *(channel->_curve->_quaternionOffset);
+                
                 if (value->_isFirstActing)
                 {
-                    do {
-                        quaternionOffset = channel->_curve->_quaternionOffsets[quaternionOffsetIndex];
-                        while (j < quaternionOffset)
-                        {
-                            if (_blendWeight != 1.0f)
-                                value->_interpolatedValue[j] *= _blendWeight;
-
-                            value->_currentValue[j] = value->_interpolatedValue[j];
-                            j++;
-                        }
+                    unsigned int j = 0;
+                    for (; j < quaternionOffset; j++)
+                    {
+                        if (_blendWeight != 1.0f)
+                            value->_interpolatedValue[j] *= _blendWeight;
 
-                        // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
+                        value->_currentValue[j] = value->_interpolatedValue[j];
+                    }
 
-                        Quaternion* interpolatedQuaternion = (Quaternion*) (value->_interpolatedValue + j);
-                        Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + 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);
+                    // 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);
+                    // Add in contribution.
+                    currentQuaternion->set(*interpolatedQuaternion);
                     
-                        // Increase by 4.
-                        j += 4;
-                        quaternionOffsetIndex++;
-                    } while (quaternionOffsetIndex < channel->_curve->_quaternionOffsetsCount);
-
                     unsigned int componentCount = value->_componentCount;
-                    // Handle remaining scalar values.
-                    while (j < componentCount)
+                    for (j += 4; j < componentCount; j++)
                     {
                         if (_blendWeight != 1.0f)
                             value->_interpolatedValue[j] *= _blendWeight;
 
                         value->_currentValue[j] = value->_interpolatedValue[j];
-                        j++;
                     }
                 }
                 else
                 {
-                    do {
-                        quaternionOffset = channel->_curve->_quaternionOffsets[quaternionOffsetIndex];
-                        while (j < quaternionOffset)
-                        {
-                            if (_blendWeight != 1.0f)
-                                value->_interpolatedValue[j] *= _blendWeight;
-
-                            value->_currentValue[j] += value->_interpolatedValue[j];
-                            j++;
-                        }
+                    unsigned int j = 0;
+                    for (; j < quaternionOffset; j++)
+                    {
+                        if (_blendWeight != 1.0f)
+                            value->_interpolatedValue[j] *= _blendWeight;
 
-                        // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
+                        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);
+                    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);
+                    // 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);
+                    // Add in contribution.
+                    currentQuaternion->multiply(*interpolatedQuaternion);
                     
-                        // Increase by 4.
-                        j += 4;
-                        quaternionOffsetIndex++;
-                    } while (quaternionOffsetIndex < channel->_curve->_quaternionOffsetsCount);
-
                     unsigned int componentCount = value->_componentCount;
-                    // Handle remaining scalar values.
-                    while (j < componentCount)
+                    for (j += 4; j < componentCount; j++)
                     {
                         if (_blendWeight != 1.0f)
                             value->_interpolatedValue[j] *= _blendWeight;
 
                         value->_currentValue[j] += value->_interpolatedValue[j];
-                        j++;
                     }
                 }
             }
         }
         else if (value->_isFirstActing)
         {
-            if (channel->_curve->_quaternionOffsetsCount == 0)
+            if (!channel->_curve->_quaternionOffset)
             {
                 memset(value->_currentValue, 0.0f, value->_componentCount);
             }
             else
             {
+                unsigned int quaternionOffset = *(channel->_curve->_quaternionOffset);
                 unsigned int j = 0;
-                unsigned int quaternionOffset = 0;
-                unsigned int quaternionOffsetIndex = 0;
-                
-                do {
-                    quaternionOffset = channel->_curve->_quaternionOffsets[quaternionOffsetIndex];
-                    while (j < quaternionOffset)
-                    {
-                        value->_currentValue[j] = 0.0f;
-                        j++;
-                    }
-
-                    // We are at the index for a quaternion component. Handle the next for components as a whole quaternion.
-                    Quaternion* currentQuaternion = (Quaternion*) (value->_currentValue + j);
+                for (; j < quaternionOffset; j++)
+                {
+                    value->_currentValue[j] = 0.0f;
+                }
 
-                    // Set it to identity.
-                    currentQuaternion->setIdentity();
-                    
-                    // Increase by 4.
-                    j += 4;
-                    quaternionOffsetIndex++;
-                } while (quaternionOffsetIndex < channel->_curve->_quaternionOffsetsCount);
+                // 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;
-                // Handle remaining scalar values.
-                while (j < componentCount)
+                for (j += 4; j < componentCount; j++)
                 {
                     value->_currentValue[j] = 0.0f;
-                    j++;
                 }
             }
         }

+ 206 - 246
gameplay/src/Curve.cpp

@@ -55,13 +55,9 @@ namespace gameplay
 {
 
 Curve::Curve(unsigned int pointCount, unsigned int componentCount)
-    : _pointCount(pointCount), _componentCount(componentCount), _componentSize(0), _quaternionOffsets(NULL), _quaternionOffsetsCount(0), _points(NULL)
+    : _pointCount(pointCount), _componentCount(componentCount), _componentSize(sizeof(float)*componentCount), _quaternionOffset(NULL), _points(NULL)
 {
-    assert(pointCount > 1 && componentCount > 0);
-
-    _componentSize = sizeof(float)*componentCount;
     _points = new Point[_pointCount];
-
     for (unsigned int i = 0; i < _pointCount; i++)
     {
         _points[i].time = 0.0f;
@@ -76,7 +72,7 @@ Curve::Curve(unsigned int pointCount, unsigned int componentCount)
 Curve::~Curve()
 {
     SAFE_DELETE_ARRAY(_points);
-    SAFE_DELETE_ARRAY(_quaternionOffsets);
+    SAFE_DELETE_ARRAY(_quaternionOffset);
 }
 
 Curve::Point::Point()
@@ -704,69 +700,17 @@ void Curve::evaluate(float time, float* dst) const
         }
     }
 
-    float* fromValue = from->value;
-    float* toValue = to->value;
-
-    if (!_quaternionOffsets)
-    {
-        for (unsigned int i = 0; i < _componentCount; i++)
-        {
-            dst[i] = lerp(t, fromValue[i], toValue[i]);
-        }
-    }
-    else
-    {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        unsigned int i = 0;
-        
-        do {
-            while (i < quaternionOffset)
-            {
-                dst[i] = lerp(t, fromValue[i], toValue[i]);
-                i++;
-            }
-            // Handle quaternion component.
-            float interpTime = lerp(t, fromValue[i], toValue[i]);
-            interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
-            i += 4;
-            quaternionOffsetIndex++;
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
-
-        while (i < _componentCount)
-        {
-            dst[i] = lerp(t, fromValue[i], toValue[i]);
-            i++;
-        }
-    }
+    interpolateLinear(t, from, to, dst);
 }
 
-void Curve::addQuaternionOffset(unsigned int offset)
+void Curve::setQuaternionOffset(unsigned int offset)
 {
-    if (!_quaternionOffsets)
-    {
-        _quaternionOffsetsCount = 1;
-        _quaternionOffsets = new unsigned int[_quaternionOffsetsCount];
-        _quaternionOffsets[0] = offset;
-    }
-    else
-    {
-        assert((offset >= _componentCount - 4) && (offset > (_quaternionOffsets[_quaternionOffsetsCount - 1] + 3)));
-
-        unsigned int oldSize = _quaternionOffsetsCount;
-        _quaternionOffsetsCount++;
-        
-        unsigned int* newArray = new unsigned int[_quaternionOffsetsCount];
-        memcpy(newArray, _quaternionOffsets, sizeof(unsigned int) * oldSize);
-        
-        // set new offset.
-        newArray[oldSize] = offset;
+    assert(offset <= (_componentCount - 4));
 
-        SAFE_DELETE_ARRAY(_quaternionOffsets);    // delete old array
-        _quaternionOffsets = newArray;  // point to new array.
-    }
+    if (!_quaternionOffset)
+        _quaternionOffset = new unsigned int[1];
+    
+    *_quaternionOffset = offset;
 }
 
 void Curve::interpolateBezier(float s, Point* from, Point* to, float* dst) const
@@ -785,38 +729,40 @@ void Curve::interpolateBezier(float s, Point* from, Point* to, float* dst) const
     float* inValue = to->inValue;
 
 
-    if (!_quaternionOffsets)
+    if (!_quaternionOffset)
     {
         for (unsigned int i = 0; i < _componentCount; i++)
         {
-            dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
         }
     }
     else
     {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
         unsigned int i = 0;
-        
-        do {
-            while (i < quaternionOffset)
-            {
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
                 dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
-                i++;
-            }
-            // Handle quaternion component.
-            float interpTime = bezier(eq1, eq2, eq3, eq4, from->time, outValue[i], to->time, inValue[i]);
-            interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
-            i += 4;
-            quaternionOffsetIndex++;
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
+        }
 
-        while (i < _componentCount)
+        // Handle quaternion component.
+        float interpTime = bezier(eq1, eq2, eq3, eq4, from->time, outValue[i], to->time, inValue[i]);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
         {
-            dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
-            i++;
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
         }
     }
 }
@@ -835,45 +781,46 @@ void Curve::interpolateBSpline(float s, Point* c0, Point* c1, Point* c2, Point*
     float* c2Value = c2->value;
     float* c3Value = c3->value;
 
-    if (!_quaternionOffsets)
+    if (!_quaternionOffset)
     {
         for (unsigned int i = 0; i < _componentCount; i++)
         {
-            dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
+            if (c1Value[i] == c2Value[i])
+                dst[i] = c1Value[i];
+            else
+                dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
         }
     }
     else
     {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
         unsigned int i = 0;
-        do {
-            while (i < quaternionOffset)
-            {
-                dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
-                i++;
-            }
-            // Handle quaternion component.
-            float interpTime;
-            if (c0->time == c1->time)
-                interpTime = bspline(eq0, eq1, eq2, eq3, -c0->time, c1->time, c2->time, c3->time);
-            else if (c2->time == c3->time)
-                interpTime = bspline(eq0, eq1, eq2, eq3, c0->time, c1->time, c2->time, -c3->time); 
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (c1Value[i] == c2Value[i])
+                dst[i] = c1Value[i];
             else
-                interpTime = bspline(eq0, eq1, eq2, eq3, c0->time, c1->time, c2->time, c3->time);
+                dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
+        }
+
+        // Handle quaternion component.
+        float interpTime;
+        if (c0->time == c1->time)
+            interpTime = bspline(eq0, eq1, eq2, eq3, -c0->time, c1->time, c2->time, c3->time);
+        else if (c2->time == c3->time)
+            interpTime = bspline(eq0, eq1, eq2, eq3, c0->time, c1->time, c2->time, -c3->time); 
+        else
+            interpTime = bspline(eq0, eq1, eq2, eq3, c0->time, c1->time, c2->time, c3->time);
+        interpolateQuaternion(s, (c1Value + i) , (c2Value + i), (dst + i));
             
-            interpolateQuaternion(s, (c1Value + i) , (c2Value + i), (dst + i));
-            i += 4;
-            quaternionOffsetIndex++;
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
-        
-        // Handle remaining scalar values.
-        while (i < _componentCount)
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
         {
-            dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
-            i++;
+            if (c1Value[i] == c2Value[i])
+                dst[i] = c1Value[i];
+            else
+                dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
         }
     }
 }
@@ -893,40 +840,40 @@ void Curve::interpolateHermite(float s, Point* from, Point* to, float* dst) cons
     float* outValue = from->outValue;
     float* inValue = to->inValue;
 
-    if (!_quaternionOffsets)
+    if (!_quaternionOffset)
     {
         for (unsigned int i = 0; i < _componentCount; i++)
         {
-            dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
         }
     }
     else
     {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
         unsigned int i = 0;
-
-        do {
-            while (i < quaternionOffset)
-            {
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
                 dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
-                i++;
-            }
-            // Handle quaternion component.
-            float interpTime = hermite(h00, h01, h10, h11, from->time, outValue[i], to->time, inValue[i]);
-            interpolateQuaternion(interpTime, (from->value + i), (to->value + i), (dst + i));
-            i += 4;
-            quaternionOffsetIndex++;
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-            
-        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
-       
-        // Handle remaining scalar values.
-        while (i < _componentCount)
+        }
+
+        // Handle quaternion component.
+        float interpTime = hermite(h00, h01, h10, h11, from->time, outValue[i], to->time, inValue[i]);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
         {
-            dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
-            i++;
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
         }
     }
 }
@@ -942,38 +889,40 @@ void Curve::interpolateHermiteFlat(float s, Point* from, Point* to, float* dst)
     float* fromValue = from->value;
     float* toValue = to->value;
 
-    if (!_quaternionOffsets)
+    if (!_quaternionOffset)
     {
         for (unsigned int i = 0; i < _componentCount; i++)
         {
-            dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
         }
     }
     else
     {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
         unsigned int i = 0;
-        float interpTime = hermiteFlat(h00, h01, from->time, to->time);
-        do {
-            while (i < quaternionOffset)
-            {
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
                 dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
-                i++;
-            }
-            // We've hit a quaternion component, so handle it. increase the component counter by 4, and increase quaternionOffsetIndex
-            interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
-            i += 4;
-            quaternionOffsetIndex++;
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
+        }
 
-        // Handle remaining scalar values.
-        while (i < _componentCount)
+        // Handle quaternion component.
+        float interpTime = hermiteFlat(h00, h01, from->time, to->time);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
         {
-            dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
-            i++;
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
         }
     }
 }
@@ -994,43 +943,51 @@ void Curve::interpolateHermiteSmooth(float s, unsigned int index, Point* from, P
     float* fromValue = from->value;
     float* toValue = to->value;
 
-    if (!_quaternionOffsets)
+    if (!_quaternionOffset)
     {
         for (unsigned int i = 0; i < _componentCount; i++)
         {
-            if (index == 0)
+            if (fromValue[i] == toValue[i])
             {
-                outValue = toValue[i] - fromValue[i];
+                dst[i] = fromValue[i];
             }
             else
             {
-                outValue = (toValue[i] - (from - 1)->value[i]) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
-            }
+                if (index == 0)
+                {
+                    outValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    outValue = (toValue[i] - (from - 1)->value[i]) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
+                }
 
-            if (index == _pointCount - 2)
-            {
-                inValue = toValue[i] - fromValue[i];
-            }
-            else
-            {
-                inValue = ((to + 1)->value[i] - fromValue[i]) * ((to->time - from->time) / ((to + 1)->time - from->time));
-            }
+                if (index == _pointCount - 2)
+                {
+                    inValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    inValue = ((to + 1)->value[i] - fromValue[i]) * ((to->time - from->time) / ((to + 1)->time - from->time));
+                }
 
-            dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
+                dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
+            }
         }
     }
     else
     {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
         unsigned int i = 0;
-
-        do {
-            // Handle scalar values up to the quaternion offset.
-            while (i < quaternionOffset)
+        for (i = 0; i < quaternionOffset; i++)
+        {   
+            if (fromValue[i] == toValue[i])
             {
-                // Interpolate as scalar.
+                dst[i] = fromValue[i];
+            }
+            else
+            {    
                 if (index == 0)
                 {
                     outValue = toValue[i] - fromValue[i];
@@ -1050,59 +1007,61 @@ void Curve::interpolateHermiteSmooth(float s, unsigned int index, Point* from, P
                 }
 
                 dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
-                i++;
-            }
-            
-            if (index == 0)
-            {
-                outValue = to->time - from->time;
-            }
-            else
-            {
-                outValue = (to->time - (from - 1)->time) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
             }
+        }
 
-            if (index == _pointCount - 2)
-            {
-                inValue = to->time - from->time;
-            }
-            else
-            {
-                inValue = ((to + 1)->time - from->time) * ((to->time - from->time) / ((to + 1)->time - from->time));
-            }
+        // Handle quaternion component.
+        if (index == 0)
+        {
+            outValue = to->time - from->time;
+        }
+        else
+        {
+            outValue = (to->time - (from - 1)->time) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
+        }
 
-            float interpTime = hermiteSmooth(h00, h01, h10, h11, from->time, outValue, to->time, inValue);
-            interpolateQuaternion(interpTime, (from->value + i), (to->value + i), (dst + i));
-            i+=4;
-            quaternionOffsetIndex++;
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
-        
+        if (index == _pointCount - 2)
+        {
+            inValue = to->time - from->time;
+        }
+        else
+        {
+            inValue = ((to + 1)->time - from->time) * ((to->time - from->time) / ((to + 1)->time - from->time));
+        }
 
-        // Handle remaining scalar values.
-        while (i < _componentCount)
+        float interpTime = hermiteSmooth(h00, h01, h10, h11, from->time, outValue, to->time, inValue);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
         {
-            // Interpolate as scalar.
-            if (index == 0)
+            if (fromValue[i] == toValue[i])
             {
-                outValue = toValue[i] - fromValue[i];
+                dst[i] = fromValue[i];
             }
             else
             {
-                outValue = (toValue[i] - (from - 1)->value[i]) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
-            }
+                // Interpolate as scalar.
+                if (index == 0)
+                {
+                    outValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    outValue = (toValue[i] - (from - 1)->value[i]) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
+                }
 
-            if (index == _pointCount - 2)
-            {
-                inValue = toValue[i] - fromValue[i];
-            }
-            else
-            {
-                inValue = ((to + 1)->value[i] - fromValue[i]) * ((to->time - from->time) / ((to + 1)->time - from->time));
-            }
+                if (index == _pointCount - 2)
+                {
+                    inValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    inValue = ((to + 1)->value[i] - fromValue[i]) * ((to->time - from->time) / ((to + 1)->time - from->time));
+                }
 
-            dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
-            i++;
+                dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
+            }
         }
     }
 }
@@ -1112,38 +1071,39 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
     float* fromValue = from->value;
     float* toValue = to->value;
 
-    if (!_quaternionOffsets)
+    if (!_quaternionOffset)
     {
         for (unsigned int i = 0; i < _componentCount; i++)
         {
-            dst[i] = lerp(s, fromValue[i], toValue[i]);
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = lerp(s, fromValue[i], toValue[i]);
         }
     }
     else
     {
-        // Interpolate values as scalars up to first quaternion offset.
-        unsigned int quaternionOffsetIndex = 0;
-        unsigned int quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
         unsigned int i = 0;
-        do {
-            // Loop through values until you hit the next quaternion offset.
-            while (i < quaternionOffset)
-            {
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = lerp(s, fromValue[i], toValue[i]);
+        }
+
+        // Handle quaternion component.
+        interpolateQuaternion(s, (fromValue + i), (toValue + i), (dst + i));
+        
+        // handle any remaining components as scalars
+        for (i += 4; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
                 dst[i] = lerp(s, fromValue[i], toValue[i]);
-                i++;
-            }
-            // Handle quaternion component.
-            interpolateQuaternion(s, (from->value + quaternionOffset), (to->value + quaternionOffset), (dst + quaternionOffset));        
-            i += 4;
-            quaternionOffsetIndex++;
-            quaternionOffset = _quaternionOffsets[quaternionOffsetIndex];
-        } while (quaternionOffsetIndex < _quaternionOffsetsCount);
-
-        // Loop through the last remaining values, if any.
-        while (i < _componentCount)
-        {
-            dst[i] = lerp(s, fromValue[i], toValue[i]);
-            i++;
         }
     }
 }

+ 12 - 14
gameplay/src/Curve.h

@@ -12,6 +12,7 @@ class Curve
     friend class Animation;
     friend class AnimationClip;
     friend class AnimationController;
+    friend class MeshSkin;
 
 public:
 
@@ -353,18 +354,6 @@ public:
      */
     void evaluate(float time, float* dst) const;
 
-    /**
-     * Adds an offset for the beginning of a Quaternion piece of data within the curve's value span at the specified
-     * index. The next four components of data starting at the given index will be interpolated as a Quaternion.
-     * This function will assert an error if the given index is less than the component size less the four components required
-     * to store a quaternion.
-     * One can set multiple offsets within the value span. However, this function will assert an error if the index
-     * are not ordered, or if the index 
-     * 
-     * @param index The index of the Quaternion rotation data.
-     */
-    void addQuaternionOffset(unsigned int index);
-
 private:
 
     /**
@@ -446,6 +435,16 @@ private:
      */ 
     int determineIndex(float time) const;
 
+    /**
+     * Sets the offset for the beginning of a Quaternion piece of data within the curve's value span at the specified
+     * index. The next four components of data starting at the given index will be interpolated as a Quaternion.
+     * This function will assert an error if the given index is greater than the component size subtracted by the four components required
+     * to store a quaternion.
+     * 
+     * @param index The index of the Quaternion rotation data.
+     */
+    void setQuaternionOffset(unsigned int index);
+
     /**
      * Gets the InterpolationType value for the given string ID
      *
@@ -457,8 +456,7 @@ private:
     unsigned int _pointCount;           // Number of points on the curve.
     unsigned int _componentCount;       // Number of components on the curve.
     unsigned int _componentSize;        // The component size (in bytes).
-    unsigned int* _quaternionOffsets;   // Offset for the rotation component.
-    unsigned int _quaternionOffsetsCount;
+    unsigned int* _quaternionOffset;    // Offset for the rotation component.
     Point* _points;                     // The points on the curve.
 };
 

+ 6 - 0
gameplay/src/Quaternion.cpp

@@ -281,6 +281,12 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
         return;
     }
 
+    if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w)
+    {
+        memcpy(dst, &q1, sizeof(float) * 4);
+        return;
+    }
+
     float halfY, alpha, beta;
     float u, f1, f2a, f2b;
     float ratio1, ratio2;