mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 20:34:07 +02:00
Merge pull request #5506 from howard-stearns/smooth-faded-animations
Smooth fading of animations in and out.
This commit is contained in:
commit
49be7c49b7
6 changed files with 73 additions and 19 deletions
|
@ -623,6 +623,12 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) {
|
|||
return value;
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
|
||||
Settings settings;
|
||||
settings.setValue("enableRig", isEnabled);
|
||||
_rig->setEnableRig(isEnabled);
|
||||
}
|
||||
|
||||
void MyAvatar::loadData() {
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
|
|
|
@ -72,6 +72,7 @@ public:
|
|||
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
|
||||
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
|
||||
void clearJointAnimationPriorities();
|
||||
Q_INVOKABLE void setEnableRigAnimations(bool isEnabled);
|
||||
|
||||
// get/set avatar data
|
||||
void saveData();
|
||||
|
|
|
@ -49,7 +49,7 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
|
|||
_jointMappings.clear();
|
||||
}
|
||||
|
||||
void AnimationHandle::setRunning(bool running) {
|
||||
void AnimationHandle::setRunning(bool running, bool doRestoreJoints) {
|
||||
if (running && isRunning()) {
|
||||
// if we're already running, this is the same as a restart
|
||||
setFrameIndex(getFirstFrame());
|
||||
|
@ -62,7 +62,9 @@ void AnimationHandle::setRunning(bool running) {
|
|||
}
|
||||
} else {
|
||||
_rig->removeRunningAnimation(getAnimationHandlePointer());
|
||||
restoreJoints();
|
||||
if (doRestoreJoints) {
|
||||
restoreJoints();
|
||||
}
|
||||
replaceMatchingPriorities(0.0f);
|
||||
}
|
||||
emit runningChanged(isRunning());
|
||||
|
@ -71,7 +73,9 @@ void AnimationHandle::setRunning(bool running) {
|
|||
AnimationHandle::AnimationHandle(RigPointer rig) :
|
||||
QObject(rig.get()),
|
||||
_rig(rig),
|
||||
_priority(1.0f)
|
||||
_priority(1.0f),
|
||||
_fade(0.0f),
|
||||
_fadePerSecond(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,10 @@ public:
|
|||
void setPriority(float priority);
|
||||
float getPriority() const { return _priority; }
|
||||
void setMix(float mix) { _mix = mix; }
|
||||
void setFade(float fade) { _fade = fade; }
|
||||
float getFade() const { return _fade; }
|
||||
void setFadePerSecond(float fadePerSecond) { _fadePerSecond = fadePerSecond; }
|
||||
float getFadePerSecond() const { return _fadePerSecond; }
|
||||
|
||||
void setMaskedJoints(const QStringList& maskedJoints);
|
||||
const QStringList& getMaskedJoints() const { return _maskedJoints; }
|
||||
|
@ -87,7 +91,7 @@ public:
|
|||
void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
|
||||
float getLastFrame() const { return _animationLoop.getLastFrame(); }
|
||||
|
||||
void setRunning(bool running);
|
||||
void setRunning(bool running, bool restoreJoints = true);
|
||||
bool isRunning() const { return _animationLoop.isRunning(); }
|
||||
|
||||
void setFrameIndex(float frameIndex) { _animationLoop.setFrameIndex(frameIndex); }
|
||||
|
@ -111,7 +115,7 @@ signals:
|
|||
public slots:
|
||||
|
||||
void start() { setRunning(true); }
|
||||
void stop() { setRunning(false); }
|
||||
void stop() { setRunning(false); _fadePerSecond = _fade = 0.0f; }
|
||||
|
||||
private:
|
||||
|
||||
|
@ -120,7 +124,9 @@ private:
|
|||
QString _role;
|
||||
QUrl _url;
|
||||
float _priority;
|
||||
float _mix;
|
||||
float _mix; // How much of this animation to blend against what is already there. 1.0 sets to just this animation.
|
||||
float _fade; // How far are we into full strength. 0.0 uses none of this animation, 1.0 (the max) is as much as possible.
|
||||
float _fadePerSecond; // How fast should _fade change? +1.0 means _fade is increasing to 1.0 in 1 second. Negative is fading out.
|
||||
|
||||
QStringList _maskedJoints;
|
||||
QVector<int> _jointMappings;
|
||||
|
|
|
@ -80,8 +80,8 @@ void Rig::startAnimation(const QString& url, float fps, float priority,
|
|||
handle->start();
|
||||
}
|
||||
|
||||
void Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) {
|
||||
AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) {
|
||||
// check for a configured animation for the role
|
||||
//qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically;
|
||||
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
|
||||
|
@ -89,7 +89,7 @@ void Rig::addAnimationByRole(const QString& role, const QString& url, float fps,
|
|||
if (startAutomatically) {
|
||||
candidate->start();
|
||||
}
|
||||
return;
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
AnimationHandlePointer handle = createAnimationHandle();
|
||||
|
@ -131,16 +131,18 @@ void Rig::addAnimationByRole(const QString& role, const QString& url, float fps,
|
|||
if (startAutomatically) {
|
||||
handle->start();
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
|
||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
||||
addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true);
|
||||
AnimationHandlePointer handle = addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true);
|
||||
handle->setFadePerSecond(1.0f); // For now. Could be individualized later.
|
||||
}
|
||||
|
||||
void Rig::stopAnimationByRole(const QString& role) {
|
||||
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
|
||||
if (handle->getRole() == role) {
|
||||
handle->stop();
|
||||
handle->setFadePerSecond(-1.0f); // For now. Could be individualized later.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,7 +168,7 @@ bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
|
|||
}
|
||||
bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this
|
||||
for (auto animation : _runningAnimations) {
|
||||
if (animation->getRole() == role) {
|
||||
if ((animation->getRole() == role) && (animation->getFadePerSecond() >= 0.0f)) { // Don't count those being faded out
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -444,13 +446,48 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
}
|
||||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
||||
int nAnimationsSoFar = 0;
|
||||
|
||||
// First normalize the fades so that they sum to 1.0.
|
||||
// update the fade data in each animation (not normalized as they are an independent propert of animation)
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
handle->setMix(1.0f / ++nAnimationsSoFar);
|
||||
handle->setPriority(1.0);
|
||||
float fadePerSecond = handle->getFadePerSecond();
|
||||
float fade = handle->getFade();
|
||||
if (fadePerSecond != 0.0f) {
|
||||
fade += fadePerSecond * deltaTime;
|
||||
if ((0.0f >= fade) || (fade >= 1.0f)) {
|
||||
fade = glm::clamp(fade, 0.0f, 1.0f);
|
||||
handle->setFadePerSecond(0.0f);
|
||||
}
|
||||
handle->setFade(fade);
|
||||
if (fade <= 0.0f) { // stop any finished animations now
|
||||
handle->setRunning(false, false); // but do not restore joints as it causes a flicker
|
||||
}
|
||||
}
|
||||
}
|
||||
// sum the remaining fade data
|
||||
float fadeTotal = 0.0f;
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
fadeTotal += handle->getFade();
|
||||
}
|
||||
float fadeSumSoFar = 0.0f;
|
||||
foreach (const AnimationHandlePointer& handle, _runningAnimations) {
|
||||
handle->setPriority(1.0f);
|
||||
float normalizedFade = handle->getFade() / fadeTotal;
|
||||
// simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step.
|
||||
// i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result.
|
||||
// The formula here for mix is based on the idea that, at each step:
|
||||
// fadeSum is to normalizedFade, as (1 - mix) is to mix
|
||||
// i.e., fadeSumSoFar/normalizedFade = (1 - mix)/mix
|
||||
// Then we solve for mix.
|
||||
// Sanity check: For the first animation, fadeSum = 0, and the mix will always be 1.
|
||||
// Sanity check: For equal blending, the formula is equivalent to mix = 1 / nAnimationsSoFar++
|
||||
float mix = 1.0f / ((fadeSumSoFar / normalizedFade) + 1.0f);
|
||||
assert((0.0f <= mix) && (mix <= 1.0f));
|
||||
fadeSumSoFar += normalizedFade;
|
||||
handle->setMix(mix);
|
||||
handle->simulate(deltaTime);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
updateJointState(i, parentTransform);
|
||||
}
|
||||
|
|
|
@ -86,9 +86,9 @@ public:
|
|||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList());
|
||||
void stopAnimationByRole(const QString& role);
|
||||
void addAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
|
||||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false);
|
||||
AnimationHandlePointer addAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
|
||||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false);
|
||||
|
||||
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
|
||||
int rootJointIndex,
|
||||
|
|
Loading…
Reference in a new issue