Removed rig animations

* Deleted AnimationHandle class
* Removed enableAnimGraph and anableRigAnimations from Menu.
* Removed *some* references to old IK system.
  But it is still used when computing collision bounding volumes
This commit is contained in:
Anthony J. Thibault 2015-11-11 15:32:57 -08:00
parent 533773d1cd
commit 11440f92f4
13 changed files with 55 additions and 788 deletions

View file

@ -2503,17 +2503,11 @@ void Application::setAvatarUpdateThreading(bool isThreaded) {
}
auto myAvatar = getMyAvatar();
bool isRigEnabled = myAvatar->getEnableRigAnimations();
bool isGraphEnabled = myAvatar->getEnableAnimGraph();
if (_avatarUpdate) {
_avatarUpdate->terminate(); // Must be before we shutdown anim graph.
}
myAvatar->setEnableRigAnimations(false);
myAvatar->setEnableAnimGraph(false);
_avatarUpdate = new AvatarUpdate();
_avatarUpdate->initialize(isThreaded);
myAvatar->setEnableRigAnimations(isRigEnabled);
myAvatar->setEnableAnimGraph(isGraphEnabled);
}
void Application::updateLOD() {

View file

@ -440,10 +440,6 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false,
qApp, SLOT(setAvatarUpdateThreading(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, false,
avatar, SLOT(setEnableRigAnimations(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, true,
avatar, SLOT(setEnableAnimGraph(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBindPose, 0, false,
avatar, SLOT(setEnableDebugDrawBindPose(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false,

View file

@ -26,6 +26,7 @@
#include "Head.h"
#include "SkeletonModel.h"
#include "world.h"
#include "Rig.h"
namespace render {
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);

View file

@ -21,7 +21,6 @@
#include <AccountManager.h>
#include <AddressManager.h>
#include <AnimationHandle.h>
#include <AudioClient.h>
#include <DependencyManager.h>
#include <display-plugins/DisplayPlugin.h>
@ -143,16 +142,9 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
}
void MyAvatar::reset(bool andReload) {
// Gather animation mode...
// This should be simpler when we have only graph animations always on.
bool isRig = _rig->getEnableRig();
// seting rig animation to true, below, will clear the graph animation menu item, so grab it now.
bool isGraph = _rig->getEnableAnimGraph() || Menu::getInstance()->isOptionChecked(MenuOption::EnableAnimGraph);
// ... and get to sane configuration where other activity won't bother us.
if (andReload) {
qApp->setRawAvatarUpdateThreading(false);
_rig->disableHands = true;
setEnableRigAnimations(true);
}
// Reset dynamic state.
@ -189,19 +181,6 @@ void MyAvatar::reset(bool andReload) {
//_bodySensorMatrix = newBodySensorMatrix;
//updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
_skeletonModel.simulate(0.1f); // non-zero
setEnableRigAnimations(false);
_skeletonModel.simulate(0.1f);
}
if (isRig) {
setEnableRigAnimations(true);
Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, true);
} else if (isGraph) {
setEnableAnimGraph(true);
Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, true);
}
if (andReload) {
_rig->disableHands = false;
qApp->setRawAvatarUpdateThreading();
}
}
@ -760,57 +739,6 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) {
// Meanwhile, the main thread will also eventually lock as it tries to render us.
// If we demand the animation from the update thread while we're locked, we'll deadlock.
// Until we untangle this, code puts the updates back on the main thread temporarilly and starts all the loading.
void MyAvatar::safelyLoadAnimations() {
/*
_rig->addAnimationByRole("idle");
_rig->addAnimationByRole("walk");
_rig->addAnimationByRole("backup");
_rig->addAnimationByRole("leftTurn");
_rig->addAnimationByRole("rightTurn");
_rig->addAnimationByRole("leftStrafe");
_rig->addAnimationByRole("rightStrafe");
*/
}
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
if (_rig->getEnableRig() == isEnabled) {
return;
}
if (isEnabled) {
qApp->setRawAvatarUpdateThreading(false);
setEnableAnimGraph(false);
Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, false);
safelyLoadAnimations();
qApp->setRawAvatarUpdateThreading();
_rig->setEnableRig(true);
} else {
_rig->setEnableRig(false);
_rig->deleteAnimations();
}
}
void MyAvatar::setEnableAnimGraph(bool isEnabled) {
if (_rig->getEnableAnimGraph() == isEnabled) {
return;
}
if (isEnabled) {
qApp->setRawAvatarUpdateThreading(false);
setEnableRigAnimations(false);
Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, false);
safelyLoadAnimations();
if (_skeletonModel.readyToAddToScene()) {
_rig->setEnableAnimGraph(true);
initAnimGraph(); // must be enabled for the init to happen
_rig->setEnableAnimGraph(false); // must be disable to safely reset threading
}
qApp->setRawAvatarUpdateThreading();
_rig->setEnableAnimGraph(true);
} else {
_rig->setEnableAnimGraph(false);
destroyAnimGraph();
}
}
void MyAvatar::setEnableDebugDrawBindPose(bool isEnabled) {
_enableDebugDrawBindPose = isEnabled;
@ -875,7 +803,7 @@ void MyAvatar::loadData() {
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
settings.endGroup();
_rig->setEnableRig(Menu::getInstance()->isOptionChecked(MenuOption::EnableRigAnimations));
setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible));
setEnableDebugDrawBindPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBindPose));
setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose));
@ -1163,16 +1091,8 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
const QString& urlString = fullAvatarURL.toString();
if (urlString.isEmpty() || (fullAvatarURL != getSkeletonModelURL())) {
bool isRigEnabled = getEnableRigAnimations();
bool isGraphEnabled = getEnableAnimGraph();
qApp->setRawAvatarUpdateThreading(false);
setEnableRigAnimations(false);
setEnableAnimGraph(false);
setSkeletonModelURL(fullAvatarURL);
setEnableRigAnimations(isRigEnabled);
setEnableAnimGraph(isGraphEnabled);
qApp->setRawAvatarUpdateThreading();
UserActivityLogger::getInstance().changedModel("skeleton", urlString);
}

View file

@ -114,10 +114,6 @@ public:
float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); }
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _rig->getAnimationHandles(); }
AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); }
void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); }
// Interrupt the current animation with a custom animation.
Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
@ -266,11 +262,8 @@ public slots:
virtual void rebuildSkeletonBody() override;
bool getEnableRigAnimations() const { return _rig->getEnableRig(); }
void setEnableRigAnimations(bool isEnabled);
bool getEnableAnimGraph() const { return _rig->getEnableAnimGraph(); }
const QString& getAnimGraphUrl() const { return _animGraphUrl; }
void setEnableAnimGraph(bool isEnabled);
void setEnableDebugDrawBindPose(bool isEnabled);
void setEnableDebugDrawAnimPose(bool isEnabled);
void setEnableMeshVisible(bool isEnabled);
@ -376,7 +369,6 @@ private:
void maybeUpdateBillboard();
void initHeadBones();
void initAnimGraph();
void safelyLoadAnimations();
// Avatar Preferences
QUrl _fullAvatarURLFromPreferences;

View file

@ -251,40 +251,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
// Don't take inputs if playing back a recording.
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
// Don't Relax toward hand positions when in animGraph mode.
if (!_rig->getEnableAnimGraph()) {
Hand* hand = _owningAvatar->getHand();
auto leftPalm = hand->getCopyOfPalmData(HandData::LeftHand);
auto rightPalm = hand->getCopyOfPalmData(HandData::RightHand);
const float HAND_RESTORATION_RATE = 0.25f;
if (!leftPalm.isActive() && !rightPalm.isActive()) {
// palms are not yet set, use mouse
if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
} else {
// transform into model-frame
glm::vec3 handPosition = glm::inverse(_rotation) * (_owningAvatar->getHandPosition() - _translation);
applyHandPosition(geometry.rightHandJointIndex, handPosition);
}
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
} else {
if (leftPalm.isActive()) {
applyPalmData(geometry.leftHandJointIndex, leftPalm);
} else {
restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
}
if (rightPalm.isActive()) {
applyPalmData(geometry.rightHandJointIndex, rightPalm);
} else {
restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
}
}
}
}
void SkeletonModel::renderIKConstraints(gpu::Batch& batch) {
@ -344,8 +310,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) {
glm::quat inverseRotation = glm::inverse(_rotation);
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
glm::quat palmRotation = inverseRotation * palm.getRotation();
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
}
void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) {

View file

@ -247,12 +247,7 @@ void PreferencesDialog::savePreferences() {
myAvatar->setLeanScale(ui.leanScaleSpin->value());
myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value());
if (myAvatar->getAnimGraphUrl() != ui.avatarAnimationEdit->text()) { // If changed, destroy the old and start with the new
bool isEnabled = myAvatar->getEnableAnimGraph();
myAvatar->setEnableAnimGraph(false);
myAvatar->setAnimGraphUrl(ui.avatarAnimationEdit->text());
if (isEnabled) {
myAvatar->setEnableAnimGraph(true);
}
}
myAvatar->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value());

View file

@ -1,218 +0,0 @@
//
// AnimationHandle.cpp
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 10/18/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AnimationHandle.h"
#include "AnimationLogging.h"
void AnimationHandle::setURL(const QUrl& url) {
if (_url != url) {
_animation = DependencyManager::get<AnimationCache>()->getAnimation(_url = url);
_animation->ensureLoading();
QObject::connect(_animation.data(), &Resource::onRefresh, this, &AnimationHandle::clearJoints);
_jointMappings.clear();
}
}
void AnimationHandle::setPriority(float priority) {
if (_priority == priority) {
return;
}
if (isRunning()) {
_rig->removeRunningAnimation(getAnimationHandlePointer());
if (priority < _priority) {
replaceMatchingPriorities(priority);
}
_priority = priority;
_rig->addRunningAnimation(getAnimationHandlePointer());
} else {
_priority = priority;
}
}
void AnimationHandle::setStartAutomatically(bool startAutomatically) {
if (startAutomatically && !isRunning()) {
// Start before setting _animationLoop value so that code in setRunning() is executed
start();
}
_animationLoop.setStartAutomatically(startAutomatically);
}
void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
_maskedJoints = maskedJoints;
_jointMappings.clear();
}
void AnimationHandle::setRunning(bool running, bool doRestoreJoints) {
if (running && isRunning() && (getFadePerSecond() >= 0.0f)) {
// if we're already running, this is the same as a restart -- unless we're fading out.
setCurrentFrame(getFirstFrame());
return;
}
_animationLoop.setRunning(running);
if (isRunning()) {
if (!_rig->isRunningAnimation(getAnimationHandlePointer())) {
_rig->addRunningAnimation(getAnimationHandlePointer());
}
} else {
_rig->removeRunningAnimation(getAnimationHandlePointer());
if (doRestoreJoints) {
restoreJoints();
}
replaceMatchingPriorities(0.0f);
}
emit runningChanged(isRunning());
}
AnimationHandle::AnimationHandle(RigPointer rig) :
QObject(rig.get()),
_rig(rig),
_priority(1.0f),
_fade(0.0f),
_fadePerSecond(0.0f)
{
}
AnimationDetails AnimationHandle::getAnimationDetails() const {
AnimationDetails details(_role, _url, getFPS(), _priority, getLoop(), getHold(),
getStartAutomatically(), getFirstFrame(), getLastFrame(), isRunning(), getCurrentFrame());
return details;
}
void AnimationHandle::setAnimationDetails(const AnimationDetails& details) {
setRole(details.role);
setURL(details.url);
setFPS(details.fps);
setPriority(details.priority);
setLoop(details.loop);
setHold(details.hold);
setStartAutomatically(details.startAutomatically);
setFirstFrame(details.firstFrame);
setLastFrame(details.lastFrame);
setRunning(details.running);
setCurrentFrame(details.currentFrame);
// NOTE: AnimationDetails doesn't support maskedJoints
//setMaskedJoints(const QStringList& maskedJoints);
}
void AnimationHandle::setJointMappings(QVector<int> jointMappings) {
_jointMappings = jointMappings;
}
QVector<int> AnimationHandle::getJointMappings() {
if (_jointMappings.isEmpty()) {
QVector<FBXJoint> animationJoints = _animation->getGeometry().joints;
for (int i = 0; i < animationJoints.count(); i++) {
_jointMappings.append(_rig->indexOfJoint(animationJoints.at(i).name));
}
}
return _jointMappings;
}
void AnimationHandle::simulate(float deltaTime) {
if (!_animation || !_animation->isLoaded()) {
return;
}
_animationLoop.simulate(deltaTime);
if (getJointMappings().isEmpty()) {
qDebug() << "AnimationHandle::simulate -- _jointMappings.isEmpty()";
return;
}
// // update the joint mappings if necessary/possible
// if (_jointMappings.isEmpty()) {
// if (_model && _model->isActive()) {
// _jointMappings = _model->getGeometry()->getJointMappings(_animation);
// }
// if (_jointMappings.isEmpty()) {
// return;
// }
// if (!_maskedJoints.isEmpty()) {
// const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry();
// for (int i = 0; i < _jointMappings.size(); i++) {
// int& mapping = _jointMappings[i];
// if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) {
// mapping = -1;
// }
// }
// }
// }
const FBXGeometry& animationGeometry = _animation->getGeometry();
if (animationGeometry.animationFrames.isEmpty()) {
stop();
return;
}
if (_animationLoop.getMaxFrameIndexHint() != animationGeometry.animationFrames.size()) {
_animationLoop.setMaxFrameIndexHint(animationGeometry.animationFrames.size());
}
// blend between the closest two frames
applyFrame(getCurrentFrame());
}
void AnimationHandle::applyFrame(float currentFrame) {
if (!_animation || !_animation->isLoaded()) {
return;
}
const FBXGeometry& animationGeometry = _animation->getGeometry();
int frameCount = animationGeometry.animationFrames.size();
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(currentFrame) % frameCount);
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(currentFrame) % frameCount);
float frameFraction = glm::fract(currentFrame);
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) { // allow missing bones
_rig->setJointRotationInConstrainedFrame(mapping,
safeMix(floorFrame.rotations.at(i),
ceilFrame.rotations.at(i),
frameFraction),
_priority,
_mix);
// This isn't working.
// glm::vec3 floorTranslationPart = floorFrame.translations.at(i) * (1.0f - frameFraction);
// glm::vec3 ceilTranslationPart = ceilFrame.translations.at(i) * frameFraction;
// glm::vec3 floorCeilFraction = floorTranslationPart + ceilTranslationPart;
// glm::vec3 defaultTrans = _rig->getJointDefaultTranslationInConstrainedFrame(i);
// glm::vec3 mixedTranslation = floorCeilFraction * (1.0f - _mix) + defaultTrans * _mix;
// _rig->setJointTranslation(mapping, true, mixedTranslation, _priority);
}
}
}
void AnimationHandle::replaceMatchingPriorities(float newPriority) {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
if (_priority == _rig->getJointAnimatinoPriority(mapping)) {
_rig->setJointAnimatinoPriority(mapping, newPriority);
}
}
}
}
void AnimationHandle::restoreJoints() {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
_rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping));
_rig->restoreJointTranslation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping));
}
}
}

View file

@ -1,138 +0,0 @@
//
// AnimationHandle.h
// libraries/animation/src/
//
// Created by Andrzej Kapolka on 10/18/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AnimationHandle_h
#define hifi_AnimationHandle_h
#include <QObject>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QVector>
#include "AnimationCache.h"
#include "AnimationLoop.h"
#include "Rig.h"
class AnimationHandle;
class Model;
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
typedef std::weak_ptr<AnimationHandle> WeakAnimationHandlePointer;
inline uint qHash(const std::shared_ptr<AnimationHandle>& a, uint seed) {
// return qHash(a.get(), seed);
AnimationHandle* strongRef = a ? a.get() : nullptr;
return qHash(strongRef, seed);
}
inline uint qHash(const std::weak_ptr<AnimationHandle>& a, uint seed) {
AnimationHandlePointer strongPointer = a.lock();
AnimationHandle* strongRef = strongPointer ? strongPointer.get() : nullptr;
return qHash(strongRef, seed);
}
// inline uint qHash(const WeakAnimationHandlePointer& handle, uint seed) {
// return qHash(handle.data(), seed);
// }
/// Represents a handle to a model animation. I.e., an Animation in use by a given Rig.
class AnimationHandle : public QObject, public std::enable_shared_from_this<AnimationHandle> {
Q_OBJECT
public:
AnimationHandle(RigPointer rig);
AnimationHandlePointer getAnimationHandlePointer() { return shared_from_this(); }
void setRole(const QString& role) { _role = role; }
const QString& getRole() const { return _role; }
void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
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; }
void setFPS(float fps) { _animationLoop.setFPS(fps); }
float getFPS() const { return _animationLoop.getFPS(); }
void setLoop(bool loop) { _animationLoop.setLoop(loop); }
bool getLoop() const { return _animationLoop.getLoop(); }
void setHold(bool hold) { _animationLoop.setHold(hold); }
bool getHold() const { return _animationLoop.getHold(); }
void setStartAutomatically(bool startAutomatically);
bool getStartAutomatically() const { return _animationLoop.getStartAutomatically(); }
void setFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); }
float getFirstFrame() const { return _animationLoop.getFirstFrame(); }
void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
float getLastFrame() const { return _animationLoop.getLastFrame(); }
void setRunning(bool running, bool restoreJoints = true);
bool isRunning() const { return _animationLoop.getRunning(); }
void setCurrentFrame(float currentFrame) { _animationLoop.setCurrentFrame(currentFrame); }
float getCurrentFrame() const { return _animationLoop.getCurrentFrame(); }
AnimationDetails getAnimationDetails() const;
void setAnimationDetails(const AnimationDetails& details);
void setJointMappings(QVector<int> jointMappings);
QVector<int> getJointMappings(); // computing if necessary
void simulate(float deltaTime);
void applyFrame(float currentFrame);
void replaceMatchingPriorities(float newPriority);
void restoreJoints();
void clearJoints() { _jointMappings.clear(); }
signals:
void runningChanged(bool running);
public slots:
void start() { setRunning(true); }
void stop() { setRunning(false); _fadePerSecond = _fade = 0.0f; }
private:
RigPointer _rig;
AnimationPointer _animation;
QString _role;
QUrl _url;
float _priority;
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;
AnimationLoop _animationLoop;
};
#endif // hifi_AnimationHandle_h

View file

@ -18,117 +18,55 @@
#include <NumericalConstants.h>
#include <DebugDraw.h>
#include "AnimationHandle.h"
#include "AnimationLogging.h"
#include "AnimSkeleton.h"
#include "AnimClip.h"
#include "IKTarget.h"
void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
if (handle->getPriority() > (*it)->getPriority()) {
handles.insert(it, handle);
return;
}
}
handles.append(handle);
}
AnimationHandlePointer Rig::createAnimationHandle() {
AnimationHandlePointer handle(new AnimationHandle(getRigPointer()));
_animationHandles.append(handle);
return handle;
}
void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) {
handle->stop();
// FIXME? Do we need to also animationHandle->clearJoints()? deleteAnimations(), below, was first written to do so, but did not first stop it.
_animationHandles.removeOne(handle);
}
void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
if (_enableAnimGraph) {
// find an unused AnimClip clipNode
std::shared_ptr<AnimClip> clip;
if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) {
_userAnimState = UserAnimState::A;
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->getChild((int)_userAnimState));
} else if (_userAnimState == UserAnimState::A) {
_userAnimState = UserAnimState::B;
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->getChild((int)_userAnimState));
}
// set parameters
clip->setLoopFlag(loop);
clip->setStartFrame(firstFrame);
clip->setEndFrame(lastFrame);
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
float timeScale = fps / REFERENCE_FRAMES_PER_SECOND;
clip->setTimeScale(timeScale);
clip->loadURL(url);
_currentUserAnimURL = url;
// notify the userAnimStateMachine the desired state.
_animVars.set("userAnimNone", false);
_animVars.set("userAnimA", _userAnimState == UserAnimState::A);
_animVars.set("userAnimB", _userAnimState == UserAnimState::B);
} else {
float priority = 1.0f;
bool hold = true;
QStringList maskedJoints;
_currentUserAnimURL = url;
// This is different than startAnimationByRole, in which we use the existing values if the animation already exists.
// Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted).
AnimationHandlePointer handle = nullptr;
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
if (candidate->getURL() == url) {
handle = candidate;
}
}
if (!handle) {
handle = createAnimationHandle();
handle->setURL(url);
}
handle->setFade(1.0f); // If you want to fade, use the startAnimationByRole system.
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints);
handle->start();
// find an unused AnimClip clipNode
std::shared_ptr<AnimClip> clip;
if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) {
_userAnimState = UserAnimState::A;
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->getChild((int)_userAnimState));
} else if (_userAnimState == UserAnimState::A) {
_userAnimState = UserAnimState::B;
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->getChild((int)_userAnimState));
}
// set parameters
clip->setLoopFlag(loop);
clip->setStartFrame(firstFrame);
clip->setEndFrame(lastFrame);
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
float timeScale = fps / REFERENCE_FRAMES_PER_SECOND;
clip->setTimeScale(timeScale);
clip->loadURL(url);
_currentUserAnimURL = url;
// notify the userAnimStateMachine the desired state.
_animVars.set("userAnimNone", false);
_animVars.set("userAnimA", _userAnimState == UserAnimState::A);
_animVars.set("userAnimB", _userAnimState == UserAnimState::B);
}
const float FRAMES_PER_SECOND = 30.0f;
const float FADE_FRAMES = 30.0f;
void Rig::restoreAnimation() {
if (_enableAnimGraph) {
if (_currentUserAnimURL != "") {
_currentUserAnimURL = "";
// notify the userAnimStateMachine the desired state.
_animVars.set("userAnimNone", true);
_animVars.set("userAnimA", false);
_animVars.set("userAnimB", false);
}
} else {
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
if (handle->getURL() == _currentUserAnimURL) {
handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking
handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // so that the updateAnimation code notices
}
}
if (_currentUserAnimURL != "") {
_currentUserAnimURL = "";
// notify the userAnimStateMachine the desired state.
_animVars.set("userAnimNone", true);
_animVars.set("userAnimA", false);
_animVars.set("userAnimB", false);
}
}
QStringList Rig::getAnimationRoles() const {
if (_enableAnimGraph && _animNode) {
if (_animNode) {
QStringList list;
_animNode->traverse([&](AnimNode::Pointer node) {
// only report clip nodes as valid roles.
@ -148,7 +86,7 @@ QStringList Rig::getAnimationRoles() const {
}
void Rig::overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
if (_enableAnimGraph && _animNode) {
if (_animNode) {
AnimNode::Pointer node = _animNode->findByName(role);
if (node) {
_origRoleAnimations[role] = node;
@ -160,11 +98,13 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f
} else {
qCWarning(animation) << "Rig::overrideRoleAnimation could not find role " << role;
}
} else {
qCWarning(animation) << "Rig::overrideRoleAnimation avatar not ready yet";
}
}
void Rig::restoreRoleAnimation(const QString& role) {
if (_enableAnimGraph && _animNode) {
if (_animNode) {
AnimNode::Pointer node = _animNode->findByName(role);
if (node) {
auto iter = _origRoleAnimations.find(role);
@ -175,43 +115,17 @@ void Rig::restoreRoleAnimation(const QString& role) {
qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role;
}
}
} else {
qCWarning(animation) << "Rig::overrideRoleAnimation avatar not ready yet";
}
}
void Rig::prefetchAnimation(const QString& url) {
if (_enableAnimGraph) {
// This will begin loading the NetworkGeometry for the given URL.
// which should speed us up if we request it later via overrideAnimation.
auto clipNode = std::make_shared<AnimClip>("prefetch", url, 0, 0, 1.0, false);
_prefetchedAnimations.push_back(clipNode);
}
}
bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.removeOne(animationHandle);
}
void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
insertSorted(_runningAnimations, animationHandle);
}
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.contains(animationHandle);
}
bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this
for (auto animation : _runningAnimations) {
if ((animation->getRole() == role) && (animation->getFadePerSecond() >= 0.0f)) { // Don't count those being faded out
return true;
}
}
return false;
}
void Rig::deleteAnimations() {
for (auto animation : _animationHandles) {
removeAnimationHandle(animation);
}
_animationHandles.clear();
// This will begin loading the NetworkGeometry for the given URL.
// which should speed us up if we request it later via overrideAnimation.
auto clipNode = std::make_shared<AnimClip>("prefetch", url, 0, 0, 1.0, false);
_prefetchedAnimations.push_back(clipNode);
}
void Rig::destroyAnimGraph() {
@ -483,8 +397,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
_lastVelocity = workingVelocity;
}
if (_enableAnimGraph) {
{
glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity;
float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT);
@ -636,47 +549,6 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
t += deltaTime;
}
if (_enableRig) {
bool isMoving = false;
glm::vec3 right = worldRotation * IDENTITY_RIGHT;
const float PERCEPTIBLE_DELTA = 0.001f;
const float PERCEPTIBLE_SPEED = 0.1f;
// Note: Separately, we've arranged for starting/stopping animations by role (as we've done here) to pick up where they've left off when fading,
// so that you wouldn't notice the start/stop if it happens fast enough (e.g., one frame). But the print below would still be noisy.
float forwardSpeed = glm::dot(workingVelocity, front);
float rightLateralSpeed = glm::dot(workingVelocity, right);
float rightTurningDelta = glm::orientedAngle(front, _lastFront, IDENTITY_UP);
float rightTurningSpeed = rightTurningDelta / deltaTime;
bool isTurning = (std::abs(rightTurningDelta) > PERCEPTIBLE_DELTA) && (std::abs(rightTurningSpeed) > PERCEPTIBLE_SPEED);
bool isStrafing = std::abs(rightLateralSpeed) > PERCEPTIBLE_SPEED;
auto updateRole = [&](const QString& role, bool isOn) {
isMoving = isMoving || isOn;
if (isOn) {
if (!isRunningRole(role)) {
qCDebug(animation) << "Rig STARTING" << role;
//startAnimationByRole(role);
}
} else {
if (isRunningRole(role)) {
qCDebug(animation) << "Rig stopping" << role;
//stopAnimationByRole(role);
}
}
};
updateRole("walk", forwardSpeed > PERCEPTIBLE_SPEED);
updateRole("backup", forwardSpeed < -PERCEPTIBLE_SPEED);
updateRole("rightTurn", isTurning && (rightTurningSpeed > 0.0f));
updateRole("leftTurn", isTurning && (rightTurningSpeed < 0.0f));
isStrafing = isStrafing && !isMoving;
updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f));
updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f));
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
}
_lastFront = front;
_lastPosition = worldPosition;
}
@ -748,12 +620,10 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh
void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
if (_enableAnimGraph) {
if (!_animNode) {
return;
}
if (_animNode) {
updateAnimationStateHandlers();
// evaluate the animation
AnimNode::Triggers triggersOut;
AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
@ -770,51 +640,6 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, 1.0f);
setJointTranslation((int)i, true, poses[i].trans, PRIORITY);
}
} else {
// 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) {
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);
// if no fadeTotal, everyone's (typically just one running) is starting at zero. In that case, blend equally.
float normalizedFade = (fadeTotal != 0.0f) ? (handle->getFade() / fadeTotal) : (1.0f / _runningAnimations.count());
assert(normalizedFade != 0.0f);
// 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++) {
@ -916,11 +741,6 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q
return;
}
if (disableHands || (_enableAnimGraph && _animSkeleton)) {
// the hand data goes through a different path: Rig::updateFromHandParameters() --> early-exit
return;
}
if (freeLineage.isEmpty()) {
return;
}
@ -1125,10 +945,8 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) {
}
updateNeckJoint(params.neckJointIndex, params);
if (_enableAnimGraph) {
_animVars.set("isTalking", params.isTalking);
_animVars.set("notIsTalking", !params.isTalking);
}
_animVars.set("isTalking", params.isTalking);
_animVars.set("notIsTalking", !params.isTalking);
}
void Rig::updateFromEyeParameters(const EyeParameters& params) {
@ -1144,21 +962,11 @@ static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f);
void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
if (_enableAnimGraph && _animSkeleton) {
if (_animSkeleton) {
glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) *
glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS));
_animVars.set("lean", absRot);
} else if (!_enableAnimGraph) {
auto& parentState = _jointStates[_jointStates[index].getParentIndex()];
// get the rotation axes in joint space and use them to adjust the rotation
glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index));
setJointRotationInConstrainedFrame(index,
glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * Z_AXIS) *
glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * X_AXIS) *
glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * Y_AXIS) *
getJointState(index).getDefaultRotation(), DEFAULT_PRIORITY);
}
}
}
@ -1224,7 +1032,7 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A
void Rig::updateNeckJoint(int index, const HeadParameters& params) {
if (index >= 0 && _jointStates[index].getParentIndex() >= 0) {
if (_enableAnimGraph && _animSkeleton) {
if (_animSkeleton) {
if (params.isInHMD) {
glm::vec3 headPos, neckPos;
@ -1272,23 +1080,6 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
_animVars.unset("neckRotation");
_animVars.set("neckType", (int)IKTarget::Type::RotationOnly);
}
} else if (!_enableAnimGraph) {
auto& state = _jointStates[index];
auto& parentState = _jointStates[state.getParentIndex()];
// get the rotation axes in joint space and use them to adjust the rotation
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) *
state.getPreTransform() * glm::mat4_cast(state.getPreRotation())));
glm::vec3 pitchYawRoll = safeEulerAngles(params.localHeadOrientation);
glm::vec3 lean = glm::radians(glm::vec3(params.leanForward, params.torsoTwist, params.leanSideways));
pitchYawRoll -= lean;
setJointRotationInConstrainedFrame(index,
glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * Z_AXIS)) *
glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * Y_AXIS)) *
glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * X_AXIS)) *
state.getDefaultRotation(), DEFAULT_PRIORITY);
}
}
}
@ -1314,7 +1105,7 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
if (_enableAnimGraph && _animSkeleton && _animNode) {
if (_animSkeleton && _animNode) {
// TODO: figure out how to obtain the yFlip from where it is actually stored
glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
@ -1387,9 +1178,6 @@ void Rig::makeAnimSkeleton(const FBXGeometry& fbxGeometry) {
}
void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) {
if (!_enableAnimGraph) {
return;
}
makeAnimSkeleton(fbxGeometry);

View file

@ -46,9 +46,6 @@
#include "AnimNodeLoader.h"
#include "SimpleMovingAverage.h"
class AnimationHandle;
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
class Rig;
typedef std::shared_ptr<Rig> RigPointer;
@ -101,18 +98,7 @@ public:
virtual ~Rig() {}
RigPointer getRigPointer() { return shared_from_this(); }
AnimationHandlePointer createAnimationHandle();
void removeAnimationHandle(const AnimationHandlePointer& handle);
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
void addRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningAnimation(AnimationHandlePointer animationHandle);
bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation.
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
void deleteAnimations();
void destroyAnimGraph();
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
void restoreAnimation();
@ -187,11 +173,6 @@ public:
virtual void updateJointState(int index, glm::mat4 rootTransform) = 0;
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
bool getEnableRig() const { return _enableRig; }
void setEnableAnimGraph(bool isEnabled) { _enableAnimGraph = isEnabled; }
bool getEnableAnimGraph() const { return _enableAnimGraph; }
void updateFromHeadParameters(const HeadParameters& params, float dt);
void updateFromEyeParameters(const EyeParameters& params);
void updateFromHandParameters(const HandParameters& params, float dt);
@ -204,7 +185,6 @@ public:
AnimNode::ConstPointer getAnimNode() const { return _animNode; }
AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics)
QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList);
void removeAnimationStateHandler(QScriptValue handler);
void animationStateHandlerResult(int identifier, QScriptValue result);
@ -230,11 +210,6 @@ public:
int _rightElbowJointIndex { -1 };
int _rightShoulderJointIndex { -1 };
QList<AnimationHandlePointer> _animationHandles;
QList<AnimationHandlePointer> _runningAnimations;
bool _enableRig { false };
bool _enableAnimGraph { false };
glm::vec3 _lastFront;
glm::vec3 _lastPosition;
glm::vec3 _lastVelocity;

View file

@ -22,7 +22,6 @@
#include <ViewFrustum.h>
#include "AbstractViewStateInterface.h"
#include "AnimationHandle.h"
#include "Model.h"
#include "MeshPartPayload.h"
@ -1101,7 +1100,6 @@ void Model::deleteGeometry() {
_blendedVertexBuffers.clear();
_rig->clearJointStates();
_meshStates.clear();
_rig->deleteAnimations();
_rig->destroyAnimGraph();
_blendedBlendshapeCoefficients.clear();
}

View file

@ -28,10 +28,10 @@
#include <render/Scene.h>
#include <Transform.h>
#include "AnimationHandle.h"
#include "GeometryCache.h"
#include "JointState.h"
#include "TextureCache.h"
#include "Rig.h"
class AbstractViewStateInterface;
class QScriptEngine;