From de7c05cb2dda4936f7af3245e485d072486936de Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 May 2014 15:30:50 -0700 Subject: [PATCH 01/10] More animation controls. --- interface/src/avatar/MyAvatar.cpp | 19 ++++++++++++++++--- interface/src/avatar/MyAvatar.h | 4 ++-- interface/src/renderer/Model.cpp | 24 +++++++++++++++++------- interface/src/renderer/Model.h | 12 ++++++++++++ interface/src/ui/AnimationsDialog.cpp | 27 +++++++++++++++++++++++++++ interface/src/ui/AnimationsDialog.h | 6 ++++++ 6 files changed, 80 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index be88a80b90..3f40bbc8ed 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -441,10 +441,12 @@ void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) { _animationHandles.removeOne(handle); } -void MyAvatar::startAnimation(const QString& url, float fps, float priority, bool loop, const QStringList& maskedJoints) { +void MyAvatar::startAnimation(const QString& url, float fps, float priority, + bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), - Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(const QStringList&, maskedJoints)); + QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), + Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(int, firstFrame), + Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints)); return; } AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); @@ -452,6 +454,9 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, boo handle->setFPS(fps); handle->setPriority(priority); handle->setLoop(loop); + handle->setHold(hold); + handle->setFirstFrame(firstFrame); + handle->setLastFrame(lastFrame); handle->setMaskedJoints(maskedJoints); handle->start(); } @@ -514,6 +519,10 @@ void MyAvatar::saveData(QSettings* settings) { settings->setValue("url", pointer->getURL()); settings->setValue("fps", pointer->getFPS()); settings->setValue("priority", pointer->getPriority()); + settings->setValue("loop", pointer->getLoop()); + settings->setValue("hold", pointer->getHold()); + settings->setValue("firstFrame", pointer->getFirstFrame()); + settings->setValue("lastFrame", pointer->getLastFrame()); settings->setValue("maskedJoints", pointer->getMaskedJoints()); } settings->endArray(); @@ -581,6 +590,10 @@ void MyAvatar::loadData(QSettings* settings) { handle->setURL(settings->value("url").toUrl()); handle->setFPS(loadSetting(settings, "fps", 30.0f)); handle->setPriority(loadSetting(settings, "priority", 1.0f)); + handle->setLoop(settings->value("loop", true).toBool()); + handle->setHold(settings->value("hold", false).toBool()); + handle->setFirstFrame(settings->value("firstFrame", 0).toInt()); + handle->setLastFrame(settings->value("lastFrame", INT_MAX).toInt()); handle->setMaskedJoints(settings->value("maskedJoints").toStringList()); } settings->endArray(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 9d6f22264f..fcbb2103d1 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -67,8 +67,8 @@ public: void removeAnimationHandle(const AnimationHandlePointer& handle); /// Allows scripts to run animations. - Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, - float priority = 1.0f, bool loop = false, const QStringList& maskedJoints = QStringList()); + Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, + bool hold = false, int firstFrame = 0, int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList()); /// Stops an animation as identified by a URL. Q_INVOKABLE void stopAnimation(const QString& url); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 900d7ff951..5b81de2c1f 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1694,7 +1694,7 @@ void AnimationHandle::setRunning(bool running) { if (!_model->_runningAnimations.contains(_self)) { insertSorted(_model->_runningAnimations, _self); } - _frameIndex = 0.0f; + _frameIndex = _firstFrame; } else { _model->_runningAnimations.removeOne(_self); @@ -1706,7 +1706,10 @@ AnimationHandle::AnimationHandle(Model* model) : _model(model), _fps(30.0f), _priority(1.0f), + _firstFrame(0), + _lastFrame(INT_MAX), _loop(false), + _hold(false), _running(false) { } @@ -1737,10 +1740,11 @@ void AnimationHandle::simulate(float deltaTime) { stop(); return; } - int ceilFrameIndex = (int)glm::ceil(_frameIndex); - if (!_loop && ceilFrameIndex >= animationGeometry.animationFrames.size()) { + int lastFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - 1); + int firstFrameIndex = qMin(_firstFrame, lastFrameIndex); + if ((!_loop && _frameIndex >= lastFrameIndex) || firstFrameIndex == lastFrameIndex) { // passed the end; apply the last frame - const FBXAnimationFrame& frame = animationGeometry.animationFrames.last(); + const FBXAnimationFrame& frame = animationGeometry.animationFrames.at(lastFrameIndex); for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { @@ -1750,14 +1754,20 @@ void AnimationHandle::simulate(float deltaTime) { } } } - stop(); + if (!_hold) { + stop(); + } return; } + int frameCount = lastFrameIndex - firstFrameIndex + 1; + _frameIndex = firstFrameIndex + glm::mod(qMax(_frameIndex - firstFrameIndex, 0.0f), (float)frameCount); + qDebug() << _frameIndex; + // blend between the closest two frames const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at( - ceilFrameIndex % animationGeometry.animationFrames.size()); + firstFrameIndex + ((int)glm::ceil(_frameIndex) - firstFrameIndex) % frameCount); const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at( - (int)glm::floor(_frameIndex) % animationGeometry.animationFrames.size()); + firstFrameIndex + ((int)glm::floor(_frameIndex) - firstFrameIndex) % frameCount); float frameFraction = glm::fract(_frameIndex); for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 59ec50cac1..979dc6c601 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -393,6 +393,15 @@ public: void setLoop(bool loop) { _loop = loop; } bool getLoop() const { return _loop; } + void setHold(bool hold) { _hold = hold; } + bool getHold() const { return _hold; } + + void setFirstFrame(int firstFrame) { _firstFrame = firstFrame; } + int getFirstFrame() const { return _firstFrame; } + + void setLastFrame(int lastFrame) { _lastFrame = lastFrame; } + int getLastFrame() const { return _lastFrame; } + void setMaskedJoints(const QStringList& maskedJoints); const QStringList& getMaskedJoints() const { return _maskedJoints; } @@ -416,7 +425,10 @@ private: QUrl _url; float _fps; float _priority; + int _firstFrame; + int _lastFrame; bool _loop; + bool _hold; QStringList _maskedJoints; bool _running; QVector _jointMappings; diff --git a/interface/src/ui/AnimationsDialog.cpp b/interface/src/ui/AnimationsDialog.cpp index 29837f67be..f30c98cce7 100644 --- a/interface/src/ui/AnimationsDialog.cpp +++ b/interface/src/ui/AnimationsDialog.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include @@ -107,6 +108,24 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo maskedJointBox->addWidget(_chooseMaskedJoints = new QPushButton("Choose")); connect(_chooseMaskedJoints, SIGNAL(clicked(bool)), SLOT(chooseMaskedJoints())); + layout->addRow("Loop:", _loop = new QCheckBox()); + _loop->setChecked(handle->getLoop()); + connect(_loop, SIGNAL(toggled(bool)), SLOT(updateHandle())); + + layout->addRow("Hold:", _hold = new QCheckBox()); + _hold->setChecked(handle->getHold()); + connect(_hold, SIGNAL(toggled(bool)), SLOT(updateHandle())); + + layout->addRow("First Frame:", _firstFrame = new QSpinBox()); + _firstFrame->setMaximum(INT_MAX); + _firstFrame->setValue(handle->getFirstFrame()); + connect(_firstFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle())); + + layout->addRow("Last Frame:", _lastFrame = new QSpinBox()); + _lastFrame->setMaximum(INT_MAX); + _lastFrame->setValue(handle->getLastFrame()); + connect(_lastFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle())); + QPushButton* remove = new QPushButton("Delete"); layout->addRow(remove); connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle())); @@ -149,7 +168,15 @@ void AnimationPanel::updateHandle() { _handle->setURL(_url->text()); _handle->setFPS(_fps->value()); _handle->setPriority(_priority->value()); + _handle->setLoop(_loop->isChecked()); + _handle->setHold(_hold->isChecked()); + _handle->setFirstFrame(_firstFrame->value()); + _handle->setLastFrame(_lastFrame->value()); _handle->setMaskedJoints(_maskedJoints->text().split(QRegExp("\\s*,\\s*"))); + + if ((_loop->isChecked() || _hold->isChecked()) && !_handle->isRunning()) { + _handle->start(); + } } void AnimationPanel::removeHandle() { diff --git a/interface/src/ui/AnimationsDialog.h b/interface/src/ui/AnimationsDialog.h index 7693a1da97..8b0f6cd257 100644 --- a/interface/src/ui/AnimationsDialog.h +++ b/interface/src/ui/AnimationsDialog.h @@ -17,9 +17,11 @@ #include "avatar/MyAvatar.h" +class QCheckBox; class QDoubleSpinner; class QLineEdit; class QPushButton; +class QSpinBox; class QVBoxLayout; /// Allows users to edit the avatar animations. @@ -64,6 +66,10 @@ private: QLineEdit* _url; QDoubleSpinBox* _fps; QDoubleSpinBox* _priority; + QCheckBox* _loop; + QCheckBox* _hold; + QSpinBox* _firstFrame; + QSpinBox* _lastFrame; QLineEdit* _maskedJoints; QPushButton* _chooseMaskedJoints; bool _applying; From 2eee9a32ac4d38da9b18dd5433a38ec36a8dc944 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 21 May 2014 15:39:12 -0700 Subject: [PATCH 02/10] Removed debugging line. --- interface/src/renderer/Model.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5b81de2c1f..5d0f571b5b 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1761,7 +1761,6 @@ void AnimationHandle::simulate(float deltaTime) { } int frameCount = lastFrameIndex - firstFrameIndex + 1; _frameIndex = firstFrameIndex + glm::mod(qMax(_frameIndex - firstFrameIndex, 0.0f), (float)frameCount); - qDebug() << _frameIndex; // blend between the closest two frames const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at( From d4af39a9f5aded75fc429fe7e89e110529a6c1a3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 May 2014 14:41:46 -0700 Subject: [PATCH 03/10] Working on handling animation priorities to prevent Hydra move/restore from interfering with scripted animations. --- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/SkeletonModel.cpp | 29 ++++++----- interface/src/renderer/Model.cpp | 66 ++++++++++++++++---------- interface/src/renderer/Model.h | 16 +++---- interface/src/ui/AnimationsDialog.cpp | 4 ++ interface/src/ui/AnimationsDialog.h | 2 + 6 files changed, 75 insertions(+), 46 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c8d1747505..b2a3ecbedb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -714,14 +714,14 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const { void MyAvatar::setJointData(int index, const glm::quat& rotation) { Avatar::setJointData(index, rotation); if (QThread::currentThread() == thread()) { - _skeletonModel.setJointState(index, true, rotation); + _skeletonModel.setJointState(index, true, rotation, 2.0f); } } void MyAvatar::clearJointData(int index) { Avatar::clearJointData(index); if (QThread::currentThread() == thread()) { - _skeletonModel.setJointState(index, false); + _skeletonModel.setJointState(index, false, glm::quat(), 2.0f); } } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 09871ad38b..429ab1cf30 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -21,6 +21,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) : _owningAvatar(owningAvatar) { } +const float PALM_PRIORITY = 3.0f; + void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { setTranslation(_owningAvatar->getPosition()); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f))); @@ -43,7 +45,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex); if (jointIndex != -1) { - setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true); + setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true, PALM_PRIORITY); } } return; @@ -58,16 +60,16 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { if (leftPalmIndex == -1) { // palms are not yet set, use mouse if (_owningAvatar->getHandState() == HAND_STATE_NULL) { - restoreRightHandPosition(HAND_RESTORATION_RATE); + restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } else { applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition()); } - restoreLeftHandPosition(HAND_RESTORATION_RATE); + restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } else if (leftPalmIndex == rightPalmIndex) { // right hand only applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]); - restoreLeftHandPosition(HAND_RESTORATION_RATE); + restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } else { applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); @@ -132,7 +134,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) if (jointIndex == -1) { return; } - setJointPosition(jointIndex, position); + setJointPosition(jointIndex, position, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::vec3 handPosition, elbowPosition; @@ -148,7 +150,8 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) // align hand with forearm float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; - applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector)); + applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), + forearmVector), true, PALM_PRIORITY); } void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { @@ -183,12 +186,14 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { } else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f); setJointPosition(parentJointIndex, palm.getPosition() + forearmVector * - geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale)); - setJointRotation(parentJointIndex, palmRotation, true); + geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale), + glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); + setJointRotation(parentJointIndex, palmRotation, true, PALM_PRIORITY); _jointStates[jointIndex].rotation = glm::quat(); } else { - setJointPosition(jointIndex, palm.getPosition(), palmRotation, true); + setJointPosition(jointIndex, palm.getPosition(), palmRotation, + true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); } } @@ -335,11 +340,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition); - setJointRotation(shoulderJointIndex, shoulderRotation, true); + setJointRotation(shoulderJointIndex, shoulderRotation, true, PALM_PRIORITY); setJointRotation(elbowJointIndex, rotationBetween(shoulderRotation * forwardVector, - wristPosition - elbowPosition) * shoulderRotation, true); + wristPosition - elbowPosition) * shoulderRotation, true, PALM_PRIORITY); - setJointRotation(jointIndex, rotation, true); + setJointRotation(jointIndex, rotation, true, PALM_PRIORITY); } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5d0f571b5b..db7e0a7c6d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -118,7 +118,7 @@ QVector Model::createJointStates(const FBXGeometry& geometry) JointState state; state.translation = joint.translation; state.rotation = joint.rotation; - state.animationDisabled = false; + state.animationPriority = 0.0f; jointStates.append(state); } @@ -473,9 +473,18 @@ bool Model::getJointState(int index, glm::quat& rotation) const { glm::abs(rotation.w - defaultRotation.w) >= EPSILON; } -void Model::setJointState(int index, bool valid, const glm::quat& rotation) { +void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { if (index != -1 && index < _jointStates.size()) { - _jointStates[index].rotation = valid ? rotation : _geometry->getFBXGeometry().joints.at(index).rotation; + JointState& state = _jointStates[index]; + if (priority >= state.animationPriority) { + if (valid) { + state.rotation = rotation; + state.animationPriority = priority; + } else if (priority == state.animationPriority) { + state.rotation = _geometry->getFBXGeometry().joints.at(index).rotation; + state.animationPriority = 0.0f; + } + } } } @@ -535,8 +544,8 @@ bool Model::getRightHandRotation(glm::quat& rotation) const { return getJointRotation(getRightHandJointIndex(), rotation); } -bool Model::restoreLeftHandPosition(float percent) { - return restoreJointPosition(getLeftHandJointIndex(), percent); +bool Model::restoreLeftHandPosition(float percent, float priority) { + return restoreJointPosition(getLeftHandJointIndex(), percent, priority); } bool Model::getLeftShoulderPosition(glm::vec3& position) const { @@ -547,8 +556,8 @@ float Model::getLeftArmLength() const { return getLimbLength(getLeftHandJointIndex()); } -bool Model::restoreRightHandPosition(float percent) { - return restoreJointPosition(getRightHandJointIndex(), percent); +bool Model::restoreRightHandPosition(float percent, float priority) { + return restoreJointPosition(getRightHandJointIndex(), percent, priority); } bool Model::getRightShoulderPosition(glm::vec3& position) const { @@ -1115,7 +1124,7 @@ void Model::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint } bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) { + int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } @@ -1138,7 +1147,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat endRotation; if (useRotation) { getJointRotation(jointIndex, endRotation, true); - applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation)); + applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation), priority); getJointRotation(jointIndex, endRotation, true); } @@ -1182,7 +1191,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const 1.0f / (combinedWeight + 1.0f)); } } - applyRotationDelta(index, combinedDelta); + applyRotationDelta(index, combinedDelta, priority); glm::quat actualDelta = state.combinedRotation * glm::inverse(oldCombinedRotation); endPosition = actualDelta * jointVector + jointPosition; if (useRotation) { @@ -1200,15 +1209,17 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const return true; } -bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind) { +bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } JointState& state = _jointStates[jointIndex]; - state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation * - glm::inverse(fromBind ? _geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation : - _geometry->getFBXGeometry().joints.at(jointIndex).inverseDefaultRotation); - state.animationDisabled = true; + if (priority >= state.animationPriority) { + state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation * + glm::inverse(fromBind ? _geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation : + _geometry->getFBXGeometry().joints.at(jointIndex).inverseDefaultRotation); + state.animationPriority = priority; + } return true; } @@ -1229,7 +1240,7 @@ void Model::setJointTranslation(int jointIndex, const glm::vec3& translation) { state.translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation; } -bool Model::restoreJointPosition(int jointIndex, float percent) { +bool Model::restoreJointPosition(int jointIndex, float percent, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; } @@ -1238,10 +1249,12 @@ bool Model::restoreJointPosition(int jointIndex, float percent) { foreach (int index, freeLineage) { JointState& state = _jointStates[index]; - const FBXJoint& joint = geometry.joints.at(index); - state.rotation = safeMix(state.rotation, joint.rotation, percent); - state.translation = glm::mix(state.translation, joint.translation, percent); - state.animationDisabled = false; + if (priority == state.animationPriority) { + const FBXJoint& joint = geometry.joints.at(index); + state.rotation = safeMix(state.rotation, joint.rotation, percent); + state.translation = glm::mix(state.translation, joint.translation, percent); + state.animationPriority = 0.0f; + } } return true; } @@ -1260,8 +1273,12 @@ float Model::getLimbLength(int jointIndex) const { return length; } -void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain) { +void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) { JointState& state = _jointStates[jointIndex]; + if (priority < state.animationPriority) { + return; + } + state.animationPriority = priority; const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex]; if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) && joint.rotationMax == glm::vec3(PI, PI, PI))) { @@ -1275,7 +1292,6 @@ void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool cons glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax)); state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation; state.rotation = newRotation; - state.animationDisabled = true; } const int BALL_SUBDIVISIONS = 10; @@ -1749,8 +1765,9 @@ void AnimationHandle::simulate(float deltaTime) { int mapping = _jointMappings.at(i); if (mapping != -1) { Model::JointState& state = _model->_jointStates[mapping]; - if (!state.animationDisabled) { + if (_priority >= state.animationPriority) { state.rotation = frame.rotations.at(i); + state.animationPriority = _priority; } } } @@ -1772,8 +1789,9 @@ void AnimationHandle::simulate(float deltaTime) { int mapping = _jointMappings.at(i); if (mapping != -1) { Model::JointState& state = _model->_jointStates[mapping]; - if (!state.animationDisabled) { + if (_priority >= state.animationPriority) { state.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + state.animationPriority = _priority; } } } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 979dc6c601..23bde4846b 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -113,7 +113,7 @@ public: bool getJointState(int index, glm::quat& rotation) const; /// Sets the joint state at the specified index. - void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat()); + void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } @@ -166,7 +166,7 @@ public: /// Restores some percentage of the default position of the left hand. /// \param percent the percentage of the default position to restore /// \return whether or not the left hand joint was found - bool restoreLeftHandPosition(float percent = 1.0f); + bool restoreLeftHandPosition(float percent = 1.0f, float priority = 1.0f); /// Gets the position of the left shoulder. /// \return whether or not the left shoulder joint was found @@ -178,7 +178,7 @@ public: /// Restores some percentage of the default position of the right hand. /// \param percent the percentage of the default position to restore /// \return whether or not the right hand joint was found - bool restoreRightHandPosition(float percent = 1.0f); + bool restoreRightHandPosition(float percent = 1.0f, float priority = 1.0f); /// Gets the position of the right shoulder. /// \return whether or not the right shoulder joint was found @@ -254,7 +254,7 @@ protected: glm::quat rotation; // rotation relative to parent glm::mat4 transform; // rotation to world frame + translation in model frame glm::quat combinedRotation; // rotation from joint local to world frame - bool animationDisabled; // if true, animations do not affect this joint + float animationPriority; // the priority of the animation affecting this joint }; bool _shapesAreDirty; @@ -290,8 +290,8 @@ protected: bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(), bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, - const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f)); - bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false); + const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); + bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false, float priority = 1.0f); void setJointTranslation(int jointIndex, const glm::vec3& translation); @@ -299,13 +299,13 @@ protected: /// \param percent the percentage of the default position to apply (i.e., 0.25f to slerp one fourth of the way to /// the original position /// \return true if the joint was found - bool restoreJointPosition(int jointIndex, float percent = 1.0f); + bool restoreJointPosition(int jointIndex, float percent = 1.0f, float priority = 0.0f); /// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's /// first free ancestor. float getLimbLength(int jointIndex) const; - void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true); + void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true, float priority = 1.0f); void computeBoundingShape(const FBXGeometry& geometry); diff --git a/interface/src/ui/AnimationsDialog.cpp b/interface/src/ui/AnimationsDialog.cpp index f30c98cce7..205a215b10 100644 --- a/interface/src/ui/AnimationsDialog.cpp +++ b/interface/src/ui/AnimationsDialog.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include #include @@ -80,6 +81,9 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); setLayout(layout); + layout->addRow("Role:", _role = new QComboBox()); + _role->setEditable(true); + QHBoxLayout* urlBox = new QHBoxLayout(); layout->addRow("URL:", urlBox); urlBox->addWidget(_url = new QLineEdit(handle->getURL().toString()), 1); diff --git a/interface/src/ui/AnimationsDialog.h b/interface/src/ui/AnimationsDialog.h index 8b0f6cd257..edda665d10 100644 --- a/interface/src/ui/AnimationsDialog.h +++ b/interface/src/ui/AnimationsDialog.h @@ -18,6 +18,7 @@ #include "avatar/MyAvatar.h" class QCheckBox; +class QComboBox; class QDoubleSpinner; class QLineEdit; class QPushButton; @@ -63,6 +64,7 @@ private: AnimationsDialog* _dialog; AnimationHandlePointer _handle; + QComboBox* _role; QLineEdit* _url; QDoubleSpinBox* _fps; QDoubleSpinBox* _priority; From 083543419d411110027545e9c269c8b7a3ae9b98 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 May 2014 15:00:36 -0700 Subject: [PATCH 04/10] More priority bits. --- interface/src/avatar/MyAvatar.cpp | 6 ++++-- interface/src/renderer/Model.cpp | 11 ++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b2a3ecbedb..06f36afe52 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -711,17 +711,19 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const { return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); } +const float JOINT_PRIORITY = 2.0f; + void MyAvatar::setJointData(int index, const glm::quat& rotation) { Avatar::setJointData(index, rotation); if (QThread::currentThread() == thread()) { - _skeletonModel.setJointState(index, true, rotation, 2.0f); + _skeletonModel.setJointState(index, true, rotation, JOINT_PRIORITY); } } void MyAvatar::clearJointData(int index) { Avatar::clearJointData(index); if (QThread::currentThread() == thread()) { - _skeletonModel.setJointState(index, false, glm::quat(), 2.0f); + _skeletonModel.setJointState(index, false, glm::quat(), JOINT_PRIORITY); } } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 835c8fd901..636f66fa16 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1681,7 +1681,7 @@ void AnimationHandle::setURL(const QUrl& url) { static void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { - if (handle->getPriority() < (*it)->getPriority()) { + if (handle->getPriority() > (*it)->getPriority()) { handles.insert(it, handle); return; } @@ -1713,6 +1713,15 @@ void AnimationHandle::setRunning(bool running) { } else { _model->_runningAnimations.removeOne(_self); + for (int i = 0; i < _jointMappings.size(); i++) { + int mapping = _jointMappings.at(i); + if (mapping != -1) { + Model::JointState& state = _model->_jointStates[mapping]; + if (_priority == state.animationPriority) { + state.animationPriority = 0.0f; + } + } + } } } From 8458477b9f7d47d4b2dd2ca806e5c265b648cf89 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 May 2014 16:15:34 -0700 Subject: [PATCH 05/10] Fixed a compile warning, provide means to start/stop animations. --- interface/src/AudioReflector.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 6 ++++-- interface/src/renderer/Model.cpp | 19 +++++++++++++++-- interface/src/renderer/Model.h | 18 ++++++++++++++-- interface/src/ui/AnimationsDialog.cpp | 30 ++++++++++++++++++++++----- interface/src/ui/AnimationsDialog.h | 4 ++++ 6 files changed, 67 insertions(+), 12 deletions(-) diff --git a/interface/src/AudioReflector.cpp b/interface/src/AudioReflector.cpp index e66735c403..52d23b4fee 100644 --- a/interface/src/AudioReflector.cpp +++ b/interface/src/AudioReflector.cpp @@ -17,7 +17,7 @@ const float MINIMUM_ATTENUATION_TO_REFLECT = 1.0f / 256.0f; const float DEFAULT_DISTANCE_SCALING_FACTOR = 2.0f; const float MAXIMUM_DELAY_MS = 1000.0 * 20.0f; // stop reflecting after path is this long const int DEFAULT_DIFFUSION_FANOUT = 5; -const int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10; +const unsigned int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10; const float DEFAULT_LOCAL_ATTENUATION_FACTOR = 0.125; const float DEFAULT_COMB_FILTER_WINDOW = 0.05f; //ms delay differential to avoid diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 06f36afe52..25eb071c8a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -430,8 +430,6 @@ void MyAvatar::setGravity(const glm::vec3& gravity) { AnimationHandlePointer MyAvatar::addAnimationHandle() { AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); - handle->setLoop(true); - handle->start(); _animationHandles.append(handle); return handle; } @@ -516,11 +514,13 @@ void MyAvatar::saveData(QSettings* settings) { for (int i = 0; i < _animationHandles.size(); i++) { settings->setArrayIndex(i); const AnimationHandlePointer& pointer = _animationHandles.at(i); + settings->setValue("role", pointer->getRole()); settings->setValue("url", pointer->getURL()); settings->setValue("fps", pointer->getFPS()); settings->setValue("priority", pointer->getPriority()); settings->setValue("loop", pointer->getLoop()); settings->setValue("hold", pointer->getHold()); + settings->setValue("startAutomatically", pointer->getStartAutomatically()); settings->setValue("firstFrame", pointer->getFirstFrame()); settings->setValue("lastFrame", pointer->getLastFrame()); settings->setValue("maskedJoints", pointer->getMaskedJoints()); @@ -587,11 +587,13 @@ void MyAvatar::loadData(QSettings* settings) { for (int i = 0; i < animationCount; i++) { settings->setArrayIndex(i); const AnimationHandlePointer& handle = _animationHandles.at(i); + handle->setRole(settings->value("role", "idle").toString()); handle->setURL(settings->value("url").toUrl()); handle->setFPS(loadSetting(settings, "fps", 30.0f)); handle->setPriority(loadSetting(settings, "priority", 1.0f)); handle->setLoop(settings->value("loop", true).toBool()); handle->setHold(settings->value("hold", false).toBool()); + handle->setStartAutomatically(settings->value("startAutomatically", true).toBool()); handle->setFirstFrame(settings->value("firstFrame", 0).toInt()); handle->setLastFrame(settings->value("lastFrame", INT_MAX).toInt()); handle->setMaskedJoints(settings->value("maskedJoints").toStringList()); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 636f66fa16..3005921d39 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1699,12 +1699,25 @@ void AnimationHandle::setPriority(float priority) { } } +void AnimationHandle::setStartAutomatically(bool startAutomatically) { + if ((_startAutomatically = startAutomatically) && !_running) { + start(); + } +} + void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) { _maskedJoints = maskedJoints; _jointMappings.clear(); } void AnimationHandle::setRunning(bool running) { + if (_running == running) { + if (running) { + // move back to the beginning + _frameIndex = _firstFrame; + } + return; + } if ((_running = running)) { if (!_model->_runningAnimations.contains(_self)) { insertSorted(_model->_runningAnimations, _self); @@ -1723,6 +1736,7 @@ void AnimationHandle::setRunning(bool running) { } } } + emit runningChanged(_running); } AnimationHandle::AnimationHandle(Model* model) : @@ -1730,10 +1744,11 @@ AnimationHandle::AnimationHandle(Model* model) : _model(model), _fps(30.0f), _priority(1.0f), - _firstFrame(0), - _lastFrame(INT_MAX), _loop(false), _hold(false), + _startAutomatically(false), + _firstFrame(0), + _lastFrame(INT_MAX), _running(false) { } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 23bde4846b..a0806ee238 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -381,6 +381,9 @@ class AnimationHandle : public QObject { public: + void setRole(const QString& role) { _role = role; } + const QString& getRole() const { return _role; } + void setURL(const QUrl& url); const QUrl& getURL() const { return _url; } @@ -396,6 +399,9 @@ public: void setHold(bool hold) { _hold = hold; } bool getHold() const { return _hold; } + void setStartAutomatically(bool startAutomatically); + bool getStartAutomatically() const { return _startAutomatically; } + void setFirstFrame(int firstFrame) { _firstFrame = firstFrame; } int getFirstFrame() const { return _firstFrame; } @@ -407,7 +413,13 @@ public: void setRunning(bool running); bool isRunning() const { return _running; } + +signals: + void runningChanged(bool running); + +public slots: + void start() { setRunning(true); } void stop() { setRunning(false); } @@ -422,13 +434,15 @@ private: Model* _model; WeakAnimationHandlePointer _self; AnimationPointer _animation; + QString _role; QUrl _url; float _fps; float _priority; - int _firstFrame; - int _lastFrame; bool _loop; bool _hold; + bool _startAutomatically; + int _firstFrame; + int _lastFrame; QStringList _maskedJoints; bool _running; QVector _jointMappings; diff --git a/interface/src/ui/AnimationsDialog.cpp b/interface/src/ui/AnimationsDialog.cpp index 205a215b10..9f62abc270 100644 --- a/interface/src/ui/AnimationsDialog.cpp +++ b/interface/src/ui/AnimationsDialog.cpp @@ -82,7 +82,11 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo setLayout(layout); layout->addRow("Role:", _role = new QComboBox()); + _role->addItem("idle"); + _role->addItem("sit"); _role->setEditable(true); + _role->setCurrentText(handle->getRole()); + connect(_role, SIGNAL(currentTextChanged(const QString&)), SLOT(updateHandle())); QHBoxLayout* urlBox = new QHBoxLayout(); layout->addRow("URL:", urlBox); @@ -120,6 +124,10 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo _hold->setChecked(handle->getHold()); connect(_hold, SIGNAL(toggled(bool)), SLOT(updateHandle())); + layout->addRow("Start Automatically:", _startAutomatically = new QCheckBox()); + _startAutomatically->setChecked(handle->getStartAutomatically()); + connect(_startAutomatically, SIGNAL(toggled(bool)), SLOT(updateHandle())); + layout->addRow("First Frame:", _firstFrame = new QSpinBox()); _firstFrame->setMaximum(INT_MAX); _firstFrame->setValue(handle->getFirstFrame()); @@ -130,9 +138,18 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo _lastFrame->setValue(handle->getLastFrame()); connect(_lastFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle())); + QHBoxLayout* buttons = new QHBoxLayout(); + layout->addRow(buttons); + buttons->addWidget(_start = new QPushButton("Start")); + _handle->connect(_start, SIGNAL(clicked(bool)), SLOT(start())); + buttons->addWidget(_stop = new QPushButton("Stop")); + _handle->connect(_stop, SIGNAL(clicked(bool)), SLOT(stop())); QPushButton* remove = new QPushButton("Delete"); - layout->addRow(remove); + buttons->addWidget(remove); connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle())); + + connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(updateStartStop())); + updateStartStop(); } void AnimationPanel::chooseURL() { @@ -168,19 +185,22 @@ void AnimationPanel::chooseMaskedJoints() { } } +void AnimationPanel::updateStartStop() { + _start->setEnabled(!_handle->isRunning()); + _stop->setEnabled(_handle->isRunning()); +} + void AnimationPanel::updateHandle() { + _handle->setRole(_role->currentText()); _handle->setURL(_url->text()); _handle->setFPS(_fps->value()); _handle->setPriority(_priority->value()); _handle->setLoop(_loop->isChecked()); _handle->setHold(_hold->isChecked()); + _handle->setStartAutomatically(_startAutomatically->isChecked()); _handle->setFirstFrame(_firstFrame->value()); _handle->setLastFrame(_lastFrame->value()); _handle->setMaskedJoints(_maskedJoints->text().split(QRegExp("\\s*,\\s*"))); - - if ((_loop->isChecked() || _hold->isChecked()) && !_handle->isRunning()) { - _handle->start(); - } } void AnimationPanel::removeHandle() { diff --git a/interface/src/ui/AnimationsDialog.h b/interface/src/ui/AnimationsDialog.h index edda665d10..6f7a2f3784 100644 --- a/interface/src/ui/AnimationsDialog.h +++ b/interface/src/ui/AnimationsDialog.h @@ -57,6 +57,7 @@ private slots: void chooseURL(); void chooseMaskedJoints(); + void updateStartStop(); void updateHandle(); void removeHandle(); @@ -70,10 +71,13 @@ private: QDoubleSpinBox* _priority; QCheckBox* _loop; QCheckBox* _hold; + QCheckBox* _startAutomatically; QSpinBox* _firstFrame; QSpinBox* _lastFrame; QLineEdit* _maskedJoints; QPushButton* _chooseMaskedJoints; + QPushButton* _start; + QPushButton* _stop; bool _applying; }; From 2a50b5451f56c4c6ff2fc5d121d56471453599fe Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 May 2014 16:41:51 -0700 Subject: [PATCH 06/10] Script commands to start/stop animations by role. --- interface/src/avatar/MyAvatar.cpp | 42 ++++++++++++++++++++++++++++++- interface/src/avatar/MyAvatar.h | 9 +++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 25eb071c8a..042d2cdc7c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -459,6 +459,47 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, handle->start(); } +void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, + bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) { + 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(int, firstFrame), + Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints)); + return; + } + // check for a configured animation for the role + foreach (const AnimationHandlePointer& handle, _animationHandles) { + if (handle->getRole() == role) { + handle->start(); + return; + } + } + // no joy; use the parameters provided + AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); + handle->setRole(role); + handle->setURL(url); + handle->setFPS(fps); + handle->setPriority(priority); + handle->setLoop(loop); + handle->setHold(hold); + handle->setFirstFrame(firstFrame); + handle->setLastFrame(lastFrame); + handle->setMaskedJoints(maskedJoints); + handle->start(); +} + +void MyAvatar::stopAnimationByRole(const QString& role) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role)); + return; + } + foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { + if (handle->getRole() == role) { + handle->stop(); + } + } +} + void MyAvatar::stopAnimation(const QString& url) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url)); @@ -467,7 +508,6 @@ void MyAvatar::stopAnimation(const QString& url) { foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { if (handle->getURL() == url) { handle->stop(); - return; } } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index fcbb2103d1..a8eb3babd0 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -73,6 +73,15 @@ public: /// Stops an animation as identified by a URL. Q_INVOKABLE void stopAnimation(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, int firstFrame = 0, + int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList()); + + /// Stops an animation identified by its role. + Q_INVOKABLE void stopAnimationByRole(const QString& role); + // get/set avatar data void saveData(QSettings* settings); void loadData(QSettings* settings); From a32dfdb7bbb51d8196bc7b7a9d1da89ee02c56d3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 22 May 2014 17:05:28 -0700 Subject: [PATCH 07/10] We should let people "start" even if the animation is already playing (to start again from the beginning). Fixed an issue with competing priorities. --- interface/src/renderer/Model.cpp | 38 +++++++++++++++++---------- interface/src/renderer/Model.h | 3 ++- interface/src/ui/AnimationsDialog.cpp | 9 ++----- interface/src/ui/AnimationsDialog.h | 1 - 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 3005921d39..5d94a9a7d0 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1690,12 +1690,19 @@ static void insertSorted(QList& handles, const Animation } void AnimationHandle::setPriority(float priority) { - if (_priority != priority) { - _priority = priority; - if (_running) { - _model->_runningAnimations.removeOne(_self); - insertSorted(_model->_runningAnimations, _self); + if (_priority == priority) { + return; + } + if (_running) { + _model->_runningAnimations.removeOne(_self); + if (priority < _priority) { + lowerPriority(priority); } + _priority = priority; + insertSorted(_model->_runningAnimations, _self); + + } else { + _priority = priority; } } @@ -1726,15 +1733,7 @@ void AnimationHandle::setRunning(bool running) { } else { _model->_runningAnimations.removeOne(_self); - for (int i = 0; i < _jointMappings.size(); i++) { - int mapping = _jointMappings.at(i); - if (mapping != -1) { - Model::JointState& state = _model->_jointStates[mapping]; - if (_priority == state.animationPriority) { - state.animationPriority = 0.0f; - } - } - } + lowerPriority(0.0f); } emit runningChanged(_running); } @@ -1820,3 +1819,14 @@ void AnimationHandle::simulate(float deltaTime) { } } +void AnimationHandle::lowerPriority(float newPriority) { + for (int i = 0; i < _jointMappings.size(); i++) { + int mapping = _jointMappings.at(i); + if (mapping != -1) { + Model::JointState& state = _model->_jointStates[mapping]; + if (_priority == state.animationPriority) { + state.animationPriority = newPriority; + } + } + } +} diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index a0806ee238..c2aeec7183 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -430,7 +430,8 @@ private: AnimationHandle(Model* model); void simulate(float deltaTime); - + void lowerPriority(float newPriority); + Model* _model; WeakAnimationHandlePointer _self; AnimationPointer _animation; diff --git a/interface/src/ui/AnimationsDialog.cpp b/interface/src/ui/AnimationsDialog.cpp index 9f62abc270..2456b589da 100644 --- a/interface/src/ui/AnimationsDialog.cpp +++ b/interface/src/ui/AnimationsDialog.cpp @@ -148,8 +148,8 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo buttons->addWidget(remove); connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle())); - connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(updateStartStop())); - updateStartStop(); + _stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool))); + _stop->setEnabled(_handle->isRunning()); } void AnimationPanel::chooseURL() { @@ -185,11 +185,6 @@ void AnimationPanel::chooseMaskedJoints() { } } -void AnimationPanel::updateStartStop() { - _start->setEnabled(!_handle->isRunning()); - _stop->setEnabled(_handle->isRunning()); -} - void AnimationPanel::updateHandle() { _handle->setRole(_role->currentText()); _handle->setURL(_url->text()); diff --git a/interface/src/ui/AnimationsDialog.h b/interface/src/ui/AnimationsDialog.h index 6f7a2f3784..dd3865741e 100644 --- a/interface/src/ui/AnimationsDialog.h +++ b/interface/src/ui/AnimationsDialog.h @@ -57,7 +57,6 @@ private slots: void chooseURL(); void chooseMaskedJoints(); - void updateStartStop(); void updateHandle(); void removeHandle(); From 2681312b54757fd2ef0b938b81d34149ce53cb3f Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 23 May 2014 00:09:15 -0700 Subject: [PATCH 08/10] Velocity damped hand positions --- examples/hydraMove.js | 2 +- interface/src/devices/SixenseManager.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index 60823ffb36..bd462f5e38 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -50,7 +50,7 @@ var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5; var LEFT_PALM = 0; -var LEFT_BUTTON_4 = 5; +var LEFT_BUTTON_4 = 4; var LEFT_BUTTON_FWD = 5; var RIGHT_PALM = 2; var RIGHT_BUTTON_4 = 10; diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 547642a1be..d2784961bd 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -120,7 +120,6 @@ void SixenseManager::update(float deltaTime) { // Rotation of Palm glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]); rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation; - palm->setRawRotation(rotation); // Compute current velocity from position change glm::vec3 rawVelocity; @@ -130,8 +129,13 @@ void SixenseManager::update(float deltaTime) { rawVelocity = glm::vec3(0.0f); } palm->setRawVelocity(rawVelocity); // meters/sec - palm->setRawPosition(position); - + + // Use a velocity sensitive filter to damp small motions and preserve large ones with + // no latency. + float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); + palm->setRawPosition(palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter)); + palm->setRawRotation(safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter)); + // use the velocity to determine whether there's any movement (if the hand isn't new) const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f; _amountMoved += rawVelocity * deltaTime; From 4d6e8a2c9e5746e230d43dba53552312203f3348 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 23 May 2014 09:42:36 -0700 Subject: [PATCH 09/10] Or rather, replaceMatchingPriorities. --- interface/src/renderer/Model.cpp | 6 +++--- interface/src/renderer/Model.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5d94a9a7d0..d18abce677 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1696,7 +1696,7 @@ void AnimationHandle::setPriority(float priority) { if (_running) { _model->_runningAnimations.removeOne(_self); if (priority < _priority) { - lowerPriority(priority); + replaceMatchingPriorities(priority); } _priority = priority; insertSorted(_model->_runningAnimations, _self); @@ -1733,7 +1733,7 @@ void AnimationHandle::setRunning(bool running) { } else { _model->_runningAnimations.removeOne(_self); - lowerPriority(0.0f); + replaceMatchingPriorities(0.0f); } emit runningChanged(_running); } @@ -1819,7 +1819,7 @@ void AnimationHandle::simulate(float deltaTime) { } } -void AnimationHandle::lowerPriority(float newPriority) { +void AnimationHandle::replaceMatchingPriorities(float newPriority) { for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index c2aeec7183..1a6642dfc6 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -430,7 +430,7 @@ private: AnimationHandle(Model* model); void simulate(float deltaTime); - void lowerPriority(float newPriority); + void replaceMatchingPriorities(float newPriority); Model* _model; WeakAnimationHandlePointer _self; From 6998dfae3cd731f0bfa997c4ec08e3341d2e276d Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Fri, 23 May 2014 09:57:47 -0700 Subject: [PATCH 10/10] add visual indicator of thrust direction --- examples/hydraMove.js | 60 ++++++++++++++++++++++++ interface/src/devices/SixenseManager.cpp | 2 +- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/examples/hydraMove.js b/examples/hydraMove.js index bd462f5e38..ed6a5a4f44 100644 --- a/examples/hydraMove.js +++ b/examples/hydraMove.js @@ -26,6 +26,7 @@ var THRUST_INCREASE_RATE = 1.05; var MAX_THRUST_MULTIPLIER = 75.0; var thrustMultiplier = INITIAL_THRUST_MULTPLIER; var grabDelta = { x: 0, y: 0, z: 0}; +var grabStartPosition = { x: 0, y: 0, z: 0}; var grabDeltaVelocity = { x: 0, y: 0, z: 0}; var grabStartRotation = { x: 0, y: 0, z: 0, w: 1}; var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1}; @@ -63,6 +64,63 @@ function printVector(text, v, decimals) { } var debug = false; +var RED_COLOR = { red: 255, green: 0, blue: 0 }; +var GRAY_COLOR = { red: 25, green: 25, blue: 25 }; +var defaultPosition = { x: 0, y: 0, z: 0}; +var RADIUS = 0.05; +var greenSphere = -1; +var redSphere = -1; + +function createDebugOverlay() { + + if (greenSphere == -1) { + greenSphere = Overlays.addOverlay("sphere", { + position: defaultPosition, + size: RADIUS, + color: GRAY_COLOR, + alpha: 1, + visible: true, + solid: true, + anchor: "MyAvatar" + }); + redSphere = Overlays.addOverlay("sphere", { + position: defaultPosition, + size: RADIUS, + color: RED_COLOR, + alpha: 1, + visible: true, + solid: true, + anchor: "MyAvatar" + }); + } +} + +function destroyDebugOverlay() { + if (greenSphere != -1) { + Overlays.deleteOverlay(greenSphere); + Overlays.deleteOverlay(redSphere); + greenSphere = -1; + redSphere = -1; + } +} + +function displayDebug() { + if (!(grabbingWithRightHand || grabbingWithLeftHand)) { + if (greenSphere != -1) { + destroyDebugOverlay(); + } + } else { + // update debug indicator + if (greenSphere == -1) { + createDebugOverlay(); + } + + var displayOffset = { x:0, y:0.5, z:-0.5 }; + + Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } ); + Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } ); + } +} function getJoystickPosition(palm) { // returns CONTROLLER_ID position in avatar local frame @@ -220,6 +278,8 @@ function flyWithHydra(deltaTime) { MyAvatar.headPitch = newPitch; } handleGrabBehavior(deltaTime); + displayDebug(); + } Script.update.connect(flyWithHydra); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index d2784961bd..8cd3cc059e 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -135,7 +135,7 @@ void SixenseManager::update(float deltaTime) { float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); palm->setRawPosition(palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter)); palm->setRawRotation(safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter)); - + // use the velocity to determine whether there's any movement (if the hand isn't new) const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f; _amountMoved += rawVelocity * deltaTime;