WIP checkpoint

This commit is contained in:
Anthony J. Thibault 2015-11-09 18:36:23 -08:00
parent af2b3bb9d5
commit eacc2cae0c
7 changed files with 140 additions and 176 deletions

View file

@ -66,7 +66,7 @@ function kneelDown() {
function standUp() {
kneeling = false;
MyAvatar.stopAnimation(KNEEL_ANIM_URL);
MyAvatar.stopAnimation();
Overlays.editOverlay(standUpButton, { visible: false });
Overlays.editOverlay(kneelDownButton, { visible: true });

View file

@ -4294,10 +4294,6 @@ void Application::stopAllScripts(bool restart) {
it.value()->stop();
qCDebug(interfaceapp) << "stopping script..." << it.key();
}
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
// whenever a script stops in case it happened to have been setting joint rotations.
// TODO: expose animation priorities and provide a layered animation control system.
getMyAvatar()->clearJointAnimationPriorities();
getMyAvatar()->clearScriptableSettings();
}
@ -4313,10 +4309,6 @@ bool Application::stopScript(const QString& scriptHash, bool restart) {
scriptEngine->stop();
stoppedScript = true;
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
// whenever a script stops in case it happened to have been setting joint rotations.
// TODO: expose animation priorities and provide a layered animation control system.
getMyAvatar()->clearJointAnimationPriorities();
}
if (_scriptEnginesHash.empty()) {
getMyAvatar()->clearScriptableSettings();

View file

@ -666,65 +666,47 @@ void MyAvatar::startAnimation(const QString& url, float fps, bool loop, float fi
_rig->startAnimation(url, fps, loop, firstFrame, lastFrame);
}
void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
void MyAvatar::stopAnimation() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startAnimationByRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url),
Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame),
Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints));
QMetaObject::invokeMethod(this, "stopAnimation");
return;
}
_rig->startAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints);
_rig->stopAnimation();
}
void MyAvatar::stopAnimationByRole(const QString& role) {
QStringList MyAvatar::getAnimationRoles() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role));
return;
}
_rig->stopAnimationByRole(role);
}
void MyAvatar::stopAnimation(const QString& url) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url));
return;
}
_rig->stopAnimation(url);
}
AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) {
AnimationDetails result;
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getAnimationDetailsByRole", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AnimationDetails, result),
Q_ARG(const QString&, role));
QStringList result;
QMetaObject::invokeMethod(this, "getAnimationRoles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QStringList, result));
return result;
}
foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) {
if (handle->getRole() == role) {
result = handle->getAnimationDetails();
break;
}
}
return result;
return _rig->getAnimationRoles();
}
AnimationDetails MyAvatar::getAnimationDetails(const QString& url) {
AnimationDetails result;
void MyAvatar::overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop,
float firstFrame, float lastFrame) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AnimationDetails, result),
Q_ARG(const QString&, url));
return result;
QMetaObject::invokeMethod(this, "overrideAnimationRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url),
Q_ARG(float, fps), Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame));
return;
}
foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) {
if (handle->getURL() == url) {
result = handle->getAnimationDetails();
break;
}
_rig->overrideAnimationRole(role, url, fps, loop, firstFrame, lastFrame);
}
void MyAvatar::restoreAnimationRole(const QString& role) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "restoreAnimationRole", Q_ARG(const QString&, role));
return;
}
return result;
_rig->restoreAnimationRole(role);
}
void MyAvatar::prefetchAnimation(const QString& url) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "prefetchAnimation", Q_ARG(const QString&, url));
return;
}
_rig->prefetchAnimation(url);
}
void MyAvatar::saveData() {
@ -797,6 +779,7 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) {
// 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");
@ -804,6 +787,7 @@ void MyAvatar::safelyLoadAnimations() {
_rig->addAnimationByRole("rightTurn");
_rig->addAnimationByRole("leftStrafe");
_rig->addAnimationByRole("rightStrafe");
*/
}
void MyAvatar::setEnableRigAnimations(bool isEnabled) {
@ -905,23 +889,6 @@ void MyAvatar::loadData() {
settings.endArray();
setAttachmentData(attachmentData);
int animationCount = settings.beginReadArray("animationHandles");
_rig->deleteAnimations();
for (int i = 0; i < animationCount; i++) {
settings.setArrayIndex(i);
_rig->addAnimationByRole(settings.value("role", "idle").toString(),
settings.value("url").toString(),
loadSetting(settings, "fps", 30.0f),
loadSetting(settings, "priority", 1.0f),
settings.value("loop", true).toBool(),
settings.value("hold", false).toBool(),
settings.value("firstFrame", 0.0f).toFloat(),
settings.value("lastFrame", INT_MAX).toFloat(),
settings.value("maskedJoints").toStringList(),
settings.value("startAutomatically", true).toBool());
}
settings.endArray();
setDisplayName(settings.value("displayName").toString());
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
@ -1177,14 +1144,7 @@ void MyAvatar::clearJointData(int index) {
}
void MyAvatar::clearJointsData() {
clearJointAnimationPriorities();
}
void MyAvatar::clearJointAnimationPriorities() {
int numStates = _skeletonModel.getJointStateCount();
for (int i = 0; i < numStates; ++i) {
_rig->clearJointAnimationPriority(i);
}
//clearJointAnimationPriorities();
}
void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) {
@ -1382,7 +1342,6 @@ void MyAvatar::setScriptedMotorFrame(QString frame) {
}
void MyAvatar::clearScriptableSettings() {
clearJointAnimationPriorities();
_scriptedMotorVelocity = glm::vec3(0.0f);
_scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE;
}

View file

@ -117,22 +117,25 @@ public:
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _rig->getAnimationHandles(); }
AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); }
void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); }
/// Allows scripts to run animations.
// Interrupt the current animation with a custom animation.
Q_INVOKABLE void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
/// Stops an animation as identified by a URL.
Q_INVOKABLE void stopAnimation(const QString& url);
// Stops the animation that was started with startAnimation and go back to the standard animation.
Q_INVOKABLE void stopAnimation();
// Returns a list of all clips that are available
Q_INVOKABLE QStringList getAnimationRoles();
// Replace an existing standard role animation with a custom one.
Q_INVOKABLE void overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
// remove an animation role override and return to the standard animation.
Q_INVOKABLE void restoreAnimationRole(const QString& role);
// prefetch animation
Q_INVOKABLE void prefetchAnimation(const QString& url);
/// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom
/// animation for the role.
Q_INVOKABLE void startAnimationByRole(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());
/// Stops an animation identified by its role.
Q_INVOKABLE void stopAnimationByRole(const QString& role);
Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
void clearJointAnimationPriorities();
// Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update.
// The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty)
// propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided.

View file

@ -33,7 +33,7 @@ class QJsonObject;
// * evaluate method, perform actual joint manipulations here and return result by reference.
// Also, append any triggers that are detected during evaluation.
class AnimNode {
class AnimNode : public std::enable_shared_from_this<AnimNode> {
public:
enum class Type {
Clip = 0,
@ -78,6 +78,30 @@ public:
void setCurrentFrame(float frame);
template <typename F>
bool traverse(F func) {
if (func(shared_from_this())) {
for (auto&& child : _children) {
if (!child->traverse(func)) {
return false;
}
}
}
return true;
}
Pointer findByName(const QString& id) {
Pointer result;
traverse([&](Pointer node) {
if (id == node->getID()) {
result = node;
return true;
}
return false;
});
return result;
}
protected:
virtual void setCurrentFrameInternal(float frame) {}

View file

@ -79,6 +79,8 @@ void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFr
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;
@ -102,79 +104,12 @@ void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFr
handle->start();
}
}
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) {
if (candidate->getRole() == role) {
if (startAutomatically) {
candidate->start();
}
return candidate;
}
}
AnimationHandlePointer handle = createAnimationHandle();
QString standard = "";
if (url.isEmpty()) { // Default animations for fight club
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/";
if (role == "walk") {
standard = base + "walk_fwd.fbx";
} else if (role == "backup") {
standard = base + "walk_bwd.fbx";
} else if (role == "leftTurn") {
standard = base + "turn_left.fbx";
} else if (role == "rightTurn") {
standard = base + "turn_right.fbx";
} else if (role == "leftStrafe") {
standard = base + "strafe_left.fbx";
} else if (role == "rightStrafe") {
standard = base + "strafe_right.fbx";
} else if (role == "idle") {
standard = base + "idle.fbx";
fps = 25.0f;
}
if (!standard.isEmpty()) {
loop = true;
}
}
handle->setRole(role);
handle->setURL(url.isEmpty() ? standard : url);
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints);
if (startAutomatically) {
handle->start();
}
return handle;
}
const float FADE_FRAMES = 30.0f;
const float FRAMES_PER_SECOND = 30.0f;
const float FADE_FRAMES = 30.0f;
void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
AnimationHandlePointer handle = addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true);
handle->setFadePerSecond(FRAMES_PER_SECOND / FADE_FRAMES); // For now. Could be individualized later.
}
void Rig::stopAnimationByRole(const QString& role) {
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
if (handle->getRole() == role) {
handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // For now. Could be individualized later.
}
}
}
void Rig::stopAnimation(const QString& url) {
void Rig::stopAnimation() {
if (_enableAnimGraph) {
if (url == _currentUserAnimURL) {
if (_currentUserAnimURL != "") {
_currentUserAnimURL = "";
// notify the userAnimStateMachine the desired state.
_animVars.set("userAnimNone", true);
@ -183,7 +118,7 @@ void Rig::stopAnimation(const QString& url) {
}
} else {
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
if (handle->getURL() == url) {
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
}
@ -191,6 +126,59 @@ void Rig::stopAnimation(const QString& url) {
}
}
QStringList Rig::getAnimationRoles() const {
if (_enableAnimGraph && _animNode) {
QStringList list;
_animNode->traverse([&](AnimNode::Pointer node) {
list.append(node->getID());
return true;
});
return list;
} else {
return QStringList();
}
}
void Rig::overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
if (_enableAnimGraph && _animNode) {
AnimNode::Pointer node = _animNode->findByName(role);
if (node) {
//_previousRoleAnimations[role] = node;
// TODO: create clip node.
// TODO: AnimNode needs the following methods.
// Pointer getParent() const;
// void swapChild(Pointer child, Pointer newChild);
//
// pseudo code
//
// auto clipNode = std::make_shared<AnimClip>(role, url, fps, firstFrame, lastFrame, loop);
// node->getParent()->swapChild(node, clipNode);
} else {
qCWarning(animation) << "Rig::overrideAnimationRole could not find role " << role;
}
}
}
void Rig::restoreAnimationRole(const QString& role) {
if (_enableAnimGraph && _animNode) {
AnimNode::Pointer node = _animNode->findByName(role);
if (node) {
// TODO: pseudo code
// origNode = _previousRoleAnimations.find(role);
// if (origNode) {
// node->getParent()->swapChild(node, origNode);
// }
}
}
}
void Rig::prefetchAnimation(const QString& url) {
if (_enableAnimGraph) {
// TODO:
}
}
bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) {
return _runningAnimations.removeOne(animationHandle);
}
@ -661,13 +649,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
if (isOn) {
if (!isRunningRole(role)) {
qCDebug(animation) << "Rig STARTING" << role;
startAnimationByRole(role);
//startAnimationByRole(role);
}
} else {
if (isRunningRole(role)) {
qCDebug(animation) << "Rig stopping" << role;
stopAnimationByRole(role);
//stopAnimationByRole(role);
}
}
};

View file

@ -113,15 +113,13 @@ public:
void deleteAnimations();
void destroyAnimGraph();
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
void stopAnimation(const QString& url);
void startAnimationByRole(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());
void stopAnimationByRole(const QString& role);
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);
void stopAnimation();
QStringList getAnimationRoles() const;
void overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
void restoreAnimationRole(const QString& role);
void prefetchAnimation(const QString& url);
void initJointStates(QVector<JointState> states, glm::mat4 rootTransform,
int rootJointIndex,