|
|
@@ -7,22 +7,27 @@
|
|
|
#include "Animation.h"
|
|
|
#include "AnimationTarget.h"
|
|
|
#include "Game.h"
|
|
|
+#include "Quaternion.h"
|
|
|
|
|
|
namespace gameplay
|
|
|
{
|
|
|
+ int AnimationClip::_crazyCounter = 0;
|
|
|
|
|
|
AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime)
|
|
|
- : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _elapsedTime(0), _runningTime(0),
|
|
|
- _channelCount(animation->_channels.size()), _repeatCount(1.0f), _speed(1.0f), _isPlaying(false), _beginListeners(NULL), _endListeners(NULL)
|
|
|
+ : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _duration(_endTime - _startTime), _repeatCount(1.0f),
|
|
|
+ _activeDuration(_duration * _repeatCount), _speed(1.0f), _isPlaying(false), _timeStarted(0), _elapsedTime(0), _runningTime(0),
|
|
|
+ _channelPriority(NULL), _crossFadeToClip(NULL), _crossFadeStart(0), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f),
|
|
|
+ _isFadingOutStarted(false), _isFadingOut(false), _isFadingIn(false), _beginListeners(NULL), _endListeners(NULL)
|
|
|
{
|
|
|
assert(0 <= startTime && startTime <= animation->_duration && 0 <= endTime && endTime <= animation->_duration);
|
|
|
-
|
|
|
- _duration = (_endTime - _startTime);
|
|
|
- _activeDuration = _duration * _repeatCount;
|
|
|
|
|
|
- for (unsigned int i = 0; i < _channelCount; i++)
|
|
|
+ unsigned int channelCount = _animation->_channels.size();
|
|
|
+ _channelPriority = new unsigned int[channelCount];
|
|
|
+
|
|
|
+ for (unsigned int i = 0; i < channelCount; i++)
|
|
|
{
|
|
|
_values.push_back(new AnimationValue(_animation->_channels[i]->_curve->getComponentCount()));
|
|
|
+ _channelPriority[i] = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -41,6 +46,8 @@ AnimationClip::~AnimationClip()
|
|
|
valueIter++;
|
|
|
}
|
|
|
|
|
|
+ SAFE_DELETE(_crossFadeToClip);
|
|
|
+ SAFE_DELETE(_channelPriority);
|
|
|
SAFE_DELETE(_beginListeners);
|
|
|
SAFE_DELETE(_endListeners);
|
|
|
}
|
|
|
@@ -123,6 +130,16 @@ float AnimationClip::getSpeed() const
|
|
|
return _speed;
|
|
|
}
|
|
|
|
|
|
+void AnimationClip::setBlendWeight(float blendWeight)
|
|
|
+{
|
|
|
+ _blendWeight = blendWeight;
|
|
|
+}
|
|
|
+
|
|
|
+float AnimationClip::getBlendWeight() const
|
|
|
+{
|
|
|
+ return _blendWeight;
|
|
|
+}
|
|
|
+
|
|
|
bool AnimationClip::isPlaying() const
|
|
|
{
|
|
|
return _isPlaying;
|
|
|
@@ -131,12 +148,52 @@ bool AnimationClip::isPlaying() const
|
|
|
void AnimationClip::play()
|
|
|
{
|
|
|
_animation->_controller->schedule(this);
|
|
|
+ _timeStarted = Game::getGameTime();
|
|
|
}
|
|
|
|
|
|
void AnimationClip::stop()
|
|
|
{
|
|
|
_animation->_controller->unschedule(this);
|
|
|
- _isPlaying = false;
|
|
|
+ if (_isPlaying)
|
|
|
+ {
|
|
|
+ _isPlaying = false;
|
|
|
+ onEnd();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
|
|
|
+{
|
|
|
+ assert(clip);
|
|
|
+
|
|
|
+ if (clip->_isPlaying && clip->_isFadingOut)
|
|
|
+ {
|
|
|
+ clip->_isFadingOut = false;
|
|
|
+ clip->_crossFadeToClip->_isFadingIn = false;
|
|
|
+ SAFE_RELEASE(clip->_crossFadeToClip);
|
|
|
+ }
|
|
|
+
|
|
|
+ // If I already have a clip I'm fading too.. release it.
|
|
|
+ if (_crossFadeToClip)
|
|
|
+ SAFE_RELEASE(_crossFadeToClip);
|
|
|
+
|
|
|
+ // Assign the clip we're fading to, and increase its ref count.
|
|
|
+ _crossFadeToClip = clip;
|
|
|
+ _crossFadeToClip->addRef();
|
|
|
+
|
|
|
+ // Set the fade in clip to fading in, and set the duration of the fade in.
|
|
|
+ _crossFadeToClip->_isFadingIn = true;
|
|
|
+
|
|
|
+ // Set this clip to fade out, and reset the elapsed time for the fade out.
|
|
|
+ _isFadingOut = true;
|
|
|
+ _crossFadeOutElapsed = 0;
|
|
|
+ _crossFadeOutDuration = duration;
|
|
|
+ _crossFadeStart = (Game::getGameTime() - _timeStarted);
|
|
|
+ _isFadingOutStarted = true;
|
|
|
+
|
|
|
+ if (!_isPlaying)
|
|
|
+ play();
|
|
|
+
|
|
|
+ _crossFadeToClip->play();
|
|
|
}
|
|
|
|
|
|
void AnimationClip::addBeginListener(AnimationClip::Listener* listener)
|
|
|
@@ -157,12 +214,19 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
|
|
|
|
|
|
bool AnimationClip::update(unsigned long elapsedTime)
|
|
|
{
|
|
|
+ float speed = _speed;
|
|
|
if (!_isPlaying)
|
|
|
+ {
|
|
|
onBegin();
|
|
|
-
|
|
|
- // Update elapsed time.
|
|
|
- _elapsedTime += elapsedTime;
|
|
|
- _runningTime += elapsedTime * _speed;
|
|
|
+ _elapsedTime = Game::getGameTime() - _timeStarted;
|
|
|
+ _runningTime = _elapsedTime * speed;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Update elapsed time.
|
|
|
+ _elapsedTime += elapsedTime;
|
|
|
+ _runningTime += elapsedTime * speed;
|
|
|
+ }
|
|
|
|
|
|
float percentComplete = 0.0f;
|
|
|
|
|
|
@@ -171,9 +235,15 @@ bool AnimationClip::update(unsigned long elapsedTime)
|
|
|
{
|
|
|
_isPlaying = false;
|
|
|
if (_speed >= 0)
|
|
|
+ {
|
|
|
percentComplete = _activeDuration % _duration; // Get's the fractional part of the final repeat.
|
|
|
+ if (percentComplete == 0.0f)
|
|
|
+ percentComplete = _duration;
|
|
|
+ }
|
|
|
else
|
|
|
+ {
|
|
|
percentComplete = 0.0f; // If we are negative speed, the end value should be 0.
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -183,20 +253,234 @@ bool AnimationClip::update(unsigned long elapsedTime)
|
|
|
|
|
|
// Add back in start time, and divide by the total animation's duration to get the actual percentage complete
|
|
|
percentComplete = (float)(_startTime + percentComplete) / (float) _animation->_duration;
|
|
|
+
|
|
|
+ if (_isFadingOut)
|
|
|
+ {
|
|
|
+ if (_isFadingOutStarted) // Calculate elapsed time since the fade out begin.
|
|
|
+ {
|
|
|
+ _crossFadeOutElapsed = (_elapsedTime - _crossFadeStart) * speed;
|
|
|
+ _isFadingOutStarted = false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // continue tracking elapsed time.
|
|
|
+ _crossFadeOutElapsed += elapsedTime * speed;
|
|
|
+ }
|
|
|
|
|
|
+ if (_crossFadeOutElapsed < _crossFadeOutDuration)
|
|
|
+ {
|
|
|
+ float tempBlendWeight = (float) (_crossFadeOutDuration - _crossFadeOutElapsed) / (float) _crossFadeOutDuration;
|
|
|
+ _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight);
|
|
|
+
|
|
|
+ // adjust the clip your blending to's weight to be a percentage of your current blend weight
|
|
|
+ if (_isFadingIn)
|
|
|
+ {
|
|
|
+ _crossFadeToClip->_blendWeight *= _blendWeight;
|
|
|
+ _blendWeight -= _crossFadeToClip->_blendWeight;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _blendWeight = tempBlendWeight;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ { // Fade done.
|
|
|
+ _crossFadeToClip->_blendWeight = 1.0f;
|
|
|
+
|
|
|
+ if (_isFadingIn)
|
|
|
+ _crossFadeToClip->_blendWeight *= _blendWeight;
|
|
|
+
|
|
|
+ _crossFadeToClip->_isFadingIn = false;
|
|
|
+ SAFE_RELEASE(_crossFadeToClip);
|
|
|
+ _blendWeight = 0.0f;
|
|
|
+ _isFadingOut = false;
|
|
|
+ _isPlaying = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Evaluate this clip.
|
|
|
Animation::Channel* channel = NULL;
|
|
|
AnimationValue* value = NULL;
|
|
|
- for (unsigned int i = 0; i < _channelCount; i++)
|
|
|
+ AnimationTarget* target = NULL;
|
|
|
+ unsigned int channelCount = _animation->_channels.size();
|
|
|
+ for (unsigned int i = 0; i < channelCount; i++)
|
|
|
{
|
|
|
channel = _animation->_channels[i];
|
|
|
- value = _values.at(i);
|
|
|
+ target = channel->_target;
|
|
|
+ value = _values[i];
|
|
|
+
|
|
|
+ // Get the current value.
|
|
|
+ target->getAnimationPropertyValue(channel->_propertyId, value);
|
|
|
|
|
|
- // Evaluate point on curve.
|
|
|
- channel->_curve->evaluate(percentComplete, value->_currentValue);
|
|
|
+ // My channel priority has changed if my priority is greater than the active animation count.
|
|
|
+ if (target->_reassignPriorities)
|
|
|
+ {
|
|
|
+ _channelPriority[i] = target->getPriority();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_blendWeight != 0.0f)
|
|
|
+ {
|
|
|
+ // Evaluate point on Curve.
|
|
|
+ channel->_curve->evaluate(percentComplete, value->_interpolatedValue);
|
|
|
+
|
|
|
+ if (channel->_curve->_quaternionOffsetsCount == 0)
|
|
|
+ {
|
|
|
+ if (_channelPriority[i] == 1)
|
|
|
+ {
|
|
|
+ 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 j = 0;
|
|
|
+ unsigned int quaternionOffsetIndex = 0;
|
|
|
+ unsigned int quaternionOffset = 0;
|
|
|
+
|
|
|
+ if (_channelPriority[i] == 1)
|
|
|
+ {
|
|
|
+ do {
|
|
|
+ quaternionOffset = channel->_curve->_quaternionOffsets[quaternionOffsetIndex];
|
|
|
+ while (j < quaternionOffset)
|
|
|
+ {
|
|
|
+ if (_blendWeight != 1.0f)
|
|
|
+ value->_interpolatedValue[j] *= _blendWeight;
|
|
|
+
|
|
|
+ value->_currentValue[j] = value->_interpolatedValue[j];
|
|
|
+ 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);
|
|
|
+
|
|
|
+ // Increase by 4.
|
|
|
+ j += 4;
|
|
|
+ quaternionOffsetIndex++;
|
|
|
+ } while (quaternionOffsetIndex < channel->_curve->_quaternionOffsetsCount);
|
|
|
+
|
|
|
+ unsigned int componentCount = value->_componentCount;
|
|
|
+ // Handle remaining scalar values.
|
|
|
+ while (j < componentCount)
|
|
|
+ {
|
|
|
+ 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++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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);
|
|
|
+
|
|
|
+ // Increase by 4.
|
|
|
+ j += 4;
|
|
|
+ quaternionOffsetIndex++;
|
|
|
+ } while (quaternionOffsetIndex < channel->_curve->_quaternionOffsetsCount);
|
|
|
+
|
|
|
+ unsigned int componentCount = value->_componentCount;
|
|
|
+ // Handle remaining scalar values.
|
|
|
+ while (j < componentCount)
|
|
|
+ {
|
|
|
+ if (_blendWeight != 1.0f)
|
|
|
+ value->_interpolatedValue[j] *= _blendWeight;
|
|
|
+
|
|
|
+ value->_currentValue[j] += value->_interpolatedValue[j];
|
|
|
+ j++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (_channelPriority[i] == 1)
|
|
|
+ {
|
|
|
+ if (channel->_curve->_quaternionOffsetsCount == 0)
|
|
|
+ {
|
|
|
+ memset(value->_currentValue, 0.0f, value->_componentCount);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ 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);
|
|
|
+
|
|
|
+ // Set it to identity.
|
|
|
+ currentQuaternion->setIdentity();
|
|
|
+
|
|
|
+ // Increase by 4.
|
|
|
+ j += 4;
|
|
|
+ quaternionOffsetIndex++;
|
|
|
+ } while (quaternionOffsetIndex < channel->_curve->_quaternionOffsetsCount);
|
|
|
+
|
|
|
+ unsigned int componentCount = value->_componentCount;
|
|
|
+ // Handle remaining scalar values.
|
|
|
+ while (j < componentCount)
|
|
|
+ {
|
|
|
+ value->_currentValue[j] = 0.0f;
|
|
|
+ j++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
// Set the animation value on the target property.
|
|
|
- channel->_target->setAnimationPropertyValue(channel->_propertyId, value);
|
|
|
+ target->setAnimationPropertyValue(channel->_propertyId, value);
|
|
|
}
|
|
|
|
|
|
// When ended. Probably should move to it's own method so we can call it when the clip is ended early.
|
|
|
@@ -223,6 +507,18 @@ void AnimationClip::onBegin()
|
|
|
_runningTime = _activeDuration;
|
|
|
}
|
|
|
|
|
|
+ AnimationTarget* target = NULL;
|
|
|
+ unsigned int channelCount = _animation->_channels.size();
|
|
|
+ // Sets the starting value.
|
|
|
+ for (unsigned int i = 0; i < channelCount; i++)
|
|
|
+ {
|
|
|
+ target = _animation->_channels[i]->_target;
|
|
|
+
|
|
|
+ target->increaseActiveAnimationCount();
|
|
|
+ _channelPriority[i] = target->getPriority();
|
|
|
+ }
|
|
|
+
|
|
|
+ // Notify begin listeners.. if any.
|
|
|
if (_beginListeners)
|
|
|
{
|
|
|
std::vector<Listener*>::iterator listener = _beginListeners->begin();
|
|
|
@@ -236,6 +532,21 @@ void AnimationClip::onBegin()
|
|
|
|
|
|
void AnimationClip::onEnd()
|
|
|
{
|
|
|
+ AnimationTarget* target = NULL;
|
|
|
+ unsigned int channelCount = _animation->_channels.size();
|
|
|
+ for (unsigned int i = 0; i < channelCount; i++)
|
|
|
+ {
|
|
|
+ target = _animation->_channels[i]->_target;
|
|
|
+
|
|
|
+ // Decrease active animation count on target and reset the channel priority
|
|
|
+ target->decreaseActiveAnimationCount();
|
|
|
+ _channelPriority[i] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ _blendWeight = 1.0f;
|
|
|
+ _timeStarted = 0;
|
|
|
+
|
|
|
+ // Notify end listeners if any.
|
|
|
if (_endListeners)
|
|
|
{
|
|
|
std::vector<Listener*>::iterator listener = _endListeners->begin();
|