mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #3307 from AndrewMeadows/inertia
JS scripts can set spine rotation
This commit is contained in:
commit
6a6670a962
9 changed files with 71 additions and 42 deletions
|
@ -3821,6 +3821,10 @@ void Application::stopAllScripts(bool restart) {
|
|||
it.value()->stop();
|
||||
qDebug() << "stopping script..." << it.key();
|
||||
}
|
||||
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
||||
// whenever a script stops in case it happened to have been setting joint rotations.
|
||||
// TODO: expose animation priorities and provide a layered animation control system.
|
||||
_myAvatar->clearJointAnimationPriorities();
|
||||
}
|
||||
|
||||
void Application::stopScript(const QString &scriptName) {
|
||||
|
@ -3828,6 +3832,10 @@ void Application::stopScript(const QString &scriptName) {
|
|||
if (_scriptEnginesHash.contains(scriptURLString)) {
|
||||
_scriptEnginesHash.value(scriptURLString)->stop();
|
||||
qDebug() << "stopping script..." << scriptName;
|
||||
// HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities
|
||||
// whenever a script stops in case it happened to have been setting joint rotations.
|
||||
// TODO: expose animation priorities and provide a layered animation control system.
|
||||
_myAvatar->clearJointAnimationPriorities();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX
|
|||
state.setRotationInConstrainedFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2]))
|
||||
* glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1]))
|
||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0]))
|
||||
* joint.rotation);
|
||||
* joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
|
@ -69,7 +69,7 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ
|
|||
glm::quat between = rotationBetween(front, lookAt);
|
||||
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
|
||||
state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
|
||||
joint.rotation);
|
||||
joint.rotation, DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
void FaceModel::updateJointState(int index) {
|
||||
|
|
|
@ -1049,36 +1049,39 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
|
|||
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
|
||||
}
|
||||
|
||||
const float JOINT_PRIORITY = 2.0f;
|
||||
const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f;
|
||||
const float RECORDER_PRIORITY = SCRIPT_PRIORITY + 1.0f;
|
||||
|
||||
void MyAvatar::setJointRotations(QVector<glm::quat> jointRotations) {
|
||||
for (int i = 0; i < jointRotations.size(); ++i) {
|
||||
if (i < _jointData.size()) {
|
||||
_skeletonModel.setJointState(i, true, jointRotations[i], JOINT_PRIORITY + 1.0f);
|
||||
}
|
||||
int numStates = glm::min(_skeletonModel.getJointStateCount(), jointRotations.size());
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
// HACK: ATM only Recorder calls setJointRotations() so we hardcode its priority here
|
||||
_skeletonModel.setJointState(i, true, jointRotations[i], RECORDER_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setJointData(int index, const glm::quat& rotation) {
|
||||
Avatar::setJointData(index, rotation);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.setJointState(index, true, rotation, JOINT_PRIORITY);
|
||||
// HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority
|
||||
_skeletonModel.setJointState(index, true, rotation, SCRIPT_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointData(int index) {
|
||||
Avatar::clearJointData(index);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.setJointState(index, false, glm::quat(), JOINT_PRIORITY);
|
||||
// HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority
|
||||
_skeletonModel.setJointState(index, false, glm::quat(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointsData() {
|
||||
for (int i = 0; i < _jointData.size(); ++i) {
|
||||
Avatar::clearJointData(i);
|
||||
if (QThread::currentThread() == thread()) {
|
||||
_skeletonModel.clearJointAnimationPriority(i);
|
||||
}
|
||||
clearJointAnimationPriorities();
|
||||
}
|
||||
|
||||
void MyAvatar::clearJointAnimationPriorities() {
|
||||
int numStates = _skeletonModel.getJointStateCount();
|
||||
for (int i = 0; i < numStates; ++i) {
|
||||
_skeletonModel.clearJointAnimationPriority(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,8 @@ public:
|
|||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData);
|
||||
|
||||
void clearJointAnimationPriorities();
|
||||
|
||||
virtual void attach(const QString& modelURL, const QString& jointName = QString(),
|
||||
const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
|
||||
bool allowDuplicates = false, bool useSaved = true);
|
||||
|
|
|
@ -51,7 +51,8 @@ void SkeletonModel::setJointStates(QVector<JointState> states) {
|
|||
}
|
||||
}
|
||||
|
||||
const float PALM_PRIORITY = 3.0f;
|
||||
const float PALM_PRIORITY = DEFAULT_PRIORITY;
|
||||
const float LEAN_PRIORITY = DEFAULT_PRIORITY;
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
setTranslation(_owningAvatar->getPosition());
|
||||
|
@ -230,7 +231,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
|||
JointState& parentState = _jointStates[parentJointIndex];
|
||||
parentState.setRotationInBindFrame(palmRotation, PALM_PRIORITY);
|
||||
// lock hand to forearm by slamming its rotation (in parent-frame) to identity
|
||||
_jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat());
|
||||
_jointStates[jointIndex].setRotationInConstrainedFrame(glm::quat(), PALM_PRIORITY);
|
||||
} else {
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
|
@ -243,7 +244,7 @@ void SkeletonModel::updateJointState(int index) {
|
|||
const JointState& parentState = _jointStates.at(joint.parentIndex);
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (index == geometry.leanJointIndex) {
|
||||
maybeUpdateLeanRotation(parentState, joint, state);
|
||||
maybeUpdateLeanRotation(parentState, state);
|
||||
|
||||
} else if (index == geometry.neckJointIndex) {
|
||||
maybeUpdateNeckRotation(parentState, joint, state);
|
||||
|
@ -260,17 +261,18 @@ void SkeletonModel::updateJointState(int index) {
|
|||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, JointState& state) {
|
||||
if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) {
|
||||
return;
|
||||
}
|
||||
// get the rotation axes in joint space and use them to adjust the rotation
|
||||
glm::mat3 axes = glm::mat3_cast(glm::quat());
|
||||
glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInConstrainedFrame()) *
|
||||
joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation)));
|
||||
state.setRotationInConstrainedFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(),
|
||||
glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(),
|
||||
glm::normalize(inverse * axes[0])) * joint.rotation);
|
||||
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||
glm::quat inverse = glm::inverse(parentState.getRotation() * state.getDefaultRotationInParentFrame());
|
||||
state.setRotationInConstrainedFrame(
|
||||
glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), inverse * zAxis)
|
||||
* glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), inverse * xAxis)
|
||||
* state.getFBXJoint().rotation, LEAN_PRIORITY);
|
||||
}
|
||||
|
||||
void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) {
|
||||
|
|
|
@ -127,7 +127,7 @@ protected:
|
|||
/// Updates the state of the joint at the specified index.
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
void maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
void maybeUpdateLeanRotation(const JointState& parentState, JointState& state);
|
||||
void maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
void maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state);
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ glm::quat JointState::getVisibleRotationInParentFrame() const {
|
|||
void JointState::restoreRotation(float fraction, float priority) {
|
||||
assert(_fbxJoint != NULL);
|
||||
if (priority == _animationPriority || _animationPriority == 0.0f) {
|
||||
setRotationInConstrainedFrame(safeMix(_rotationInConstrainedFrame, _fbxJoint->rotation, fraction));
|
||||
setRotationInConstrainedFrameInternal(safeMix(_rotationInConstrainedFrame, _fbxJoint->rotation, fraction));
|
||||
_animationPriority = 0.0f;
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ void JointState::setRotationInBindFrame(const glm::quat& rotation, float priorit
|
|||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
setRotationInConstrainedFrame(targetRotation);
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, floa
|
|||
_rotation = delta * getRotation();
|
||||
return;
|
||||
}
|
||||
setRotationInConstrainedFrame(targetRotation);
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
}
|
||||
|
||||
/// Applies delta rotation to joint but mixes a little bit of the default pose as well.
|
||||
|
@ -208,7 +208,7 @@ void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float
|
|||
if (_constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
setRotationInConstrainedFrame(targetRotation);
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
}
|
||||
|
||||
void JointState::mixVisibleRotationDelta(const glm::quat& delta, float mixFactor) {
|
||||
|
@ -232,7 +232,17 @@ glm::quat JointState::computeVisibleParentRotation() const {
|
|||
return _visibleRotation * glm::inverse(_fbxJoint->preRotation * _visibleRotationInConstrainedFrame * _fbxJoint->postRotation);
|
||||
}
|
||||
|
||||
void JointState::setRotationInConstrainedFrame(const glm::quat& targetRotation) {
|
||||
void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain) {
|
||||
if (priority >= _animationPriority || _animationPriority == 0.0f) {
|
||||
if (constrain && _constraint) {
|
||||
_constraint->softClamp(targetRotation, _rotationInConstrainedFrame, 0.5f);
|
||||
}
|
||||
setRotationInConstrainedFrameInternal(targetRotation);
|
||||
_animationPriority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
void JointState::setRotationInConstrainedFrameInternal(const glm::quat& targetRotation) {
|
||||
glm::quat parentRotation = computeParentRotation();
|
||||
_rotationInConstrainedFrame = targetRotation;
|
||||
_transformChanged = true;
|
||||
|
@ -254,6 +264,11 @@ const bool JointState::rotationIsDefault(const glm::quat& rotation, float tolera
|
|||
glm::abs(rotation.w - defaultRotation.w) < tolerance;
|
||||
}
|
||||
|
||||
glm::quat JointState::getDefaultRotationInParentFrame() const {
|
||||
// NOTE: the result is constant and could be cached in the FBXJoint
|
||||
return _fbxJoint->preRotation * _fbxJoint->rotation * _fbxJoint->postRotation;
|
||||
}
|
||||
|
||||
const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const {
|
||||
assert(_fbxJoint != NULL);
|
||||
return _fbxJoint->translation;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <FBXReader.h>
|
||||
|
||||
const float DEFAULT_PRIORITY = 3.0f;
|
||||
|
||||
class AngularConstraint;
|
||||
|
||||
class JointState {
|
||||
|
@ -81,13 +83,14 @@ public:
|
|||
/// NOTE: the JointState's model-frame transform/rotation are NOT updated!
|
||||
void setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain = false);
|
||||
|
||||
void setRotationInConstrainedFrame(const glm::quat& targetRotation);
|
||||
void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false);
|
||||
void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation);
|
||||
const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; }
|
||||
const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; }
|
||||
|
||||
const bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const;
|
||||
|
||||
glm::quat getDefaultRotationInParentFrame() const;
|
||||
const glm::vec3& getDefaultTranslationInConstrainedFrame() const;
|
||||
|
||||
|
||||
|
@ -103,6 +106,7 @@ public:
|
|||
glm::quat computeVisibleParentRotation() const;
|
||||
|
||||
private:
|
||||
void setRotationInConstrainedFrameInternal(const glm::quat& targetRotation);
|
||||
/// debug helper function
|
||||
void loadBindRotation();
|
||||
|
||||
|
|
|
@ -438,7 +438,7 @@ void Model::reset() {
|
|||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation);
|
||||
_jointStates[i].setRotationInConstrainedFrame(geometry.joints.at(i).rotation, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -695,8 +695,7 @@ bool Model::getVisibleJointState(int index, glm::quat& rotation) const {
|
|||
void Model::clearJointState(int index) {
|
||||
if (index != -1 && index < _jointStates.size()) {
|
||||
JointState& state = _jointStates[index];
|
||||
state.setRotationInConstrainedFrame(glm::quat());
|
||||
state._animationPriority = 0.0f;
|
||||
state.setRotationInConstrainedFrame(glm::quat(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,8 +710,7 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa
|
|||
JointState& state = _jointStates[index];
|
||||
if (priority >= state._animationPriority) {
|
||||
if (valid) {
|
||||
state.setRotationInConstrainedFrame(rotation);
|
||||
state._animationPriority = priority;
|
||||
state.setRotationInConstrainedFrame(rotation, priority);
|
||||
} else {
|
||||
state.restoreRotation(1.0f, priority);
|
||||
}
|
||||
|
@ -1745,10 +1743,7 @@ void AnimationHandle::applyFrame(float frameIndex) {
|
|||
int mapping = _jointMappings.at(i);
|
||||
if (mapping != -1) {
|
||||
JointState& state = _model->_jointStates[mapping];
|
||||
if (_priority >= state._animationPriority) {
|
||||
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction));
|
||||
state._animationPriority = _priority;
|
||||
}
|
||||
state.setRotationInConstrainedFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction), _priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue