mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge pull request #2905 from ey6es/animenu
Added animation controls for start/stop frames, loop/hold flags, "start automatically," roles (with script commands to start/stop by role).
This commit is contained in:
commit
3a638cc447
8 changed files with 286 additions and 70 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -441,10 +439,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,10 +452,54 @@ 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();
|
||||
}
|
||||
|
||||
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));
|
||||
|
@ -464,7 +508,6 @@ void MyAvatar::stopAnimation(const QString& url) {
|
|||
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
|
||||
if (handle->getURL() == url) {
|
||||
handle->stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -511,9 +554,15 @@ 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());
|
||||
}
|
||||
settings->endArray();
|
||||
|
@ -578,9 +627,15 @@ 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());
|
||||
}
|
||||
settings->endArray();
|
||||
|
@ -698,17 +753,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);
|
||||
_skeletonModel.setJointState(index, true, rotation, JOINT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointData(int index) {
|
||||
Avatar::clearJointData(index);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.setJointState(index, false);
|
||||
_skeletonModel.setJointState(index, false, glm::quat(), JOINT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,12 +67,21 @@ 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);
|
||||
|
||||
/// 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ QVector<Model::JointState> 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 {
|
||||
|
@ -1114,7 +1123,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;
|
||||
}
|
||||
|
@ -1137,7 +1146,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);
|
||||
}
|
||||
|
||||
|
@ -1181,7 +1190,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) {
|
||||
|
@ -1199,15 +1208,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;
|
||||
}
|
||||
|
||||
|
@ -1228,7 +1239,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;
|
||||
}
|
||||
|
@ -1237,10 +1248,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;
|
||||
}
|
||||
|
@ -1259,8 +1272,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))) {
|
||||
|
@ -1274,7 +1291,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;
|
||||
|
@ -1665,7 +1681,7 @@ void AnimationHandle::setURL(const QUrl& url) {
|
|||
|
||||
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
|
||||
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
|
||||
if (handle->getPriority() < (*it)->getPriority()) {
|
||||
if (handle->getPriority() > (*it)->getPriority()) {
|
||||
handles.insert(it, handle);
|
||||
return;
|
||||
}
|
||||
|
@ -1674,12 +1690,25 @@ static void insertSorted(QList<AnimationHandlePointer>& 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) {
|
||||
replaceMatchingPriorities(priority);
|
||||
}
|
||||
_priority = priority;
|
||||
insertSorted(_model->_runningAnimations, _self);
|
||||
|
||||
} else {
|
||||
_priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationHandle::setStartAutomatically(bool startAutomatically) {
|
||||
if ((_startAutomatically = startAutomatically) && !_running) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1689,15 +1718,24 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
_frameIndex = 0.0f;
|
||||
_frameIndex = _firstFrame;
|
||||
|
||||
} else {
|
||||
_model->_runningAnimations.removeOne(_self);
|
||||
replaceMatchingPriorities(0.0f);
|
||||
}
|
||||
emit runningChanged(_running);
|
||||
}
|
||||
|
||||
AnimationHandle::AnimationHandle(Model* model) :
|
||||
|
@ -1706,6 +1744,10 @@ AnimationHandle::AnimationHandle(Model* model) :
|
|||
_fps(30.0f),
|
||||
_priority(1.0f),
|
||||
_loop(false),
|
||||
_hold(false),
|
||||
_startAutomatically(false),
|
||||
_firstFrame(0),
|
||||
_lastFrame(INT_MAX),
|
||||
_running(false) {
|
||||
}
|
||||
|
||||
|
@ -1736,36 +1778,55 @@ 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) {
|
||||
Model::JointState& state = _model->_jointStates[mapping];
|
||||
if (!state.animationDisabled) {
|
||||
if (_priority >= state.animationPriority) {
|
||||
state.rotation = frame.rotations.at(i);
|
||||
state.animationPriority = _priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop();
|
||||
if (!_hold) {
|
||||
stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
int frameCount = lastFrameIndex - firstFrameIndex + 1;
|
||||
_frameIndex = firstFrameIndex + glm::mod(qMax(_frameIndex - firstFrameIndex, 0.0f), (float)frameCount);
|
||||
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationHandle::replaceMatchingPriorities(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -393,12 +396,30 @@ public:
|
|||
void setLoop(bool loop) { _loop = loop; }
|
||||
bool getLoop() const { return _loop; }
|
||||
|
||||
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; }
|
||||
|
||||
void setLastFrame(int lastFrame) { _lastFrame = lastFrame; }
|
||||
int getLastFrame() const { return _lastFrame; }
|
||||
|
||||
void setMaskedJoints(const QStringList& maskedJoints);
|
||||
const QStringList& getMaskedJoints() const { return _maskedJoints; }
|
||||
|
||||
void setRunning(bool running);
|
||||
bool isRunning() const { return _running; }
|
||||
|
||||
signals:
|
||||
|
||||
void runningChanged(bool running);
|
||||
|
||||
public slots:
|
||||
|
||||
void start() { setRunning(true); }
|
||||
void stop() { setRunning(false); }
|
||||
|
||||
|
@ -409,14 +430,20 @@ private:
|
|||
AnimationHandle(Model* model);
|
||||
|
||||
void simulate(float deltaTime);
|
||||
|
||||
void replaceMatchingPriorities(float newPriority);
|
||||
|
||||
Model* _model;
|
||||
WeakAnimationHandlePointer _self;
|
||||
AnimationPointer _animation;
|
||||
QString _role;
|
||||
QUrl _url;
|
||||
float _fps;
|
||||
float _priority;
|
||||
bool _loop;
|
||||
bool _hold;
|
||||
bool _startAutomatically;
|
||||
int _firstFrame;
|
||||
int _lastFrame;
|
||||
QStringList _maskedJoints;
|
||||
bool _running;
|
||||
QVector<int> _jointMappings;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QFileDialog>
|
||||
|
@ -79,6 +81,13 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
|
|||
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
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);
|
||||
urlBox->addWidget(_url = new QLineEdit(handle->getURL().toString()), 1);
|
||||
|
@ -107,9 +116,40 @@ 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("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());
|
||||
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()));
|
||||
|
||||
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()));
|
||||
|
||||
_stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool)));
|
||||
_stop->setEnabled(_handle->isRunning());
|
||||
}
|
||||
|
||||
void AnimationPanel::chooseURL() {
|
||||
|
@ -146,9 +186,15 @@ void AnimationPanel::chooseMaskedJoints() {
|
|||
}
|
||||
|
||||
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*")));
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
#include "avatar/MyAvatar.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDoubleSpinner;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QSpinBox;
|
||||
class QVBoxLayout;
|
||||
|
||||
/// Allows users to edit the avatar animations.
|
||||
|
@ -61,11 +64,19 @@ private:
|
|||
|
||||
AnimationsDialog* _dialog;
|
||||
AnimationHandlePointer _handle;
|
||||
QComboBox* _role;
|
||||
QLineEdit* _url;
|
||||
QDoubleSpinBox* _fps;
|
||||
QDoubleSpinBox* _priority;
|
||||
QCheckBox* _loop;
|
||||
QCheckBox* _hold;
|
||||
QCheckBox* _startAutomatically;
|
||||
QSpinBox* _firstFrame;
|
||||
QSpinBox* _lastFrame;
|
||||
QLineEdit* _maskedJoints;
|
||||
QPushButton* _chooseMaskedJoints;
|
||||
QPushButton* _start;
|
||||
QPushButton* _stop;
|
||||
bool _applying;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue