From 50dd8eba4586c9494efdd5bfa31ba1793daa246a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 12 Sep 2015 08:23:50 -0700 Subject: [PATCH 01/10] Relay joint translations across network. Apply animation's root-joint translation to avatar. --- .../src/avatars/ScriptableAvatar.cpp | 14 +- interface/src/avatar/Avatar.cpp | 17 +- interface/src/avatar/Avatar.h | 1 + interface/src/avatar/MyAvatar.cpp | 37 ++- interface/src/avatar/MyAvatar.h | 5 +- interface/src/avatar/SkeletonModel.cpp | 1 + libraries/animation/src/AnimClip.cpp | 8 +- .../animation/src/AnimInverseKinematics.cpp | 3 + libraries/animation/src/AnimManipulator.cpp | 2 + libraries/animation/src/AnimationHandle.cpp | 11 + libraries/animation/src/AvatarRig.cpp | 27 +- libraries/animation/src/AvatarRig.h | 4 + libraries/animation/src/EntityRig.cpp | 14 + libraries/animation/src/EntityRig.h | 1 + libraries/animation/src/JointState.cpp | 32 ++- libraries/animation/src/JointState.h | 14 +- libraries/animation/src/Rig.cpp | 37 ++- libraries/animation/src/Rig.h | 12 +- libraries/avatars/src/AvatarData.cpp | 264 ++++++++++++++++-- libraries/avatars/src/AvatarData.h | 15 +- libraries/avatars/src/Player.cpp | 11 +- libraries/avatars/src/Recording.cpp | 14 +- libraries/avatars/src/Recording.h | 5 +- .../src/RenderableModelEntityItem.cpp | 9 +- libraries/entities/src/ModelEntityItem.cpp | 33 ++- libraries/entities/src/ModelEntityItem.h | 5 +- libraries/fbx/src/FBXReader.cpp | 39 ++- libraries/fbx/src/FBXReader.h | 2 +- .../networking/src/udt/PacketHeaders.cpp | 3 + libraries/render-utils/src/Model.cpp | 16 +- libraries/render-utils/src/Model.h | 5 +- libraries/shared/src/GLMHelpers.cpp | 2 + libraries/shared/src/GLMHelpers.h | 6 + 33 files changed, 585 insertions(+), 84 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 0b7af01c94..36a527009e 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -77,7 +77,19 @@ void ScriptableAvatar::update(float deltatime) { int mapping = animationJoints.indexOf(modelJoints[i]); if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) { JointData& data = _jointData[i]; - data.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + + auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + auto newTranslation = floorFrame.translations.at(i) * (1.0f - frameFraction) + + ceilFrame.translations.at(i) * frameFraction; + + if (data.rotation != newRotation) { + data.rotation = newRotation; + data.rotationSet = true; + } + if (data.translation != newTranslation) { + data.translation = newTranslation; + data.translationSet = true; + } } } } else { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 0d1c3b8a20..faf4bffeba 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -202,11 +202,14 @@ void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("skeleton"); for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData.at(i); - _skeletonModel.setJointState(i, true, data.rotation); + _skeletonModel.setJointRotation(i, data.rotationSet, data.rotation, 1.0f); + _skeletonModel.setJointTranslation(i, data.translationSet, data.translation, 1.0f); } - _skeletonModel.simulate(deltaTime, _hasNewJointRotations); + + _skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations); simulateAttachments(deltaTime); _hasNewJointRotations = false; + _hasNewJointTranslations = false; } { PerformanceTimer perfTimer("head"); @@ -879,6 +882,16 @@ glm::quat Avatar::getJointRotation(int index) const { return rotation; } +glm::vec3 Avatar::getJointTranslation(int index) const { + if (QThread::currentThread() != thread()) { + return AvatarData::getJointTranslation(index); + } + glm::vec3 translation; + _skeletonModel.getJointTranslation(index, translation); + return translation; +} + + int Avatar::getJointIndex(const QString& name) const { if (QThread::currentThread() != thread()) { int result; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 6cfbf939a2..5ff3f37ef5 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -115,6 +115,7 @@ public: virtual QVector getJointRotations() const; virtual glm::quat getJointRotation(int index) const; + virtual glm::vec3 getJointTranslation(int index) const; virtual int getJointIndex(const QString& name) const; virtual QStringList getJointNames() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4cf1b69ce4..e6013638c8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -239,9 +239,11 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model _jointData.resize(_rig->getJointStateCount()); + for (int i = 0; i < _jointData.size(); i++) { JointData& data = _jointData[i]; - _rig->getJointStateRotation(i, data.rotation); + data.rotationSet |= _rig->getJointStateRotation(i, data.rotation); + data.translationSet |= _rig->getJointStateTranslation(i, data.translation); } } @@ -1083,21 +1085,43 @@ void MyAvatar::setJointRotations(QVector jointRotations) { 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); + _skeletonModel.setJointRotation(i, true, jointRotations[i], RECORDER_PRIORITY); } } -void MyAvatar::setJointData(int index, const glm::quat& rotation) { +void MyAvatar::setJointTranslations(QVector jointTranslations) { + int numStates = glm::min(_skeletonModel.getJointStateCount(), jointTranslations.size()); + for (int i = 0; i < numStates; ++i) { + // HACK: ATM only Recorder calls setJointTranslations() so we hardcode its priority here + _skeletonModel.setJointTranslation(i, true, jointTranslations[i], RECORDER_PRIORITY); + } +} + +void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { if (QThread::currentThread() == thread()) { // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, true, rotation, SCRIPT_PRIORITY); + _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); + } +} + +void MyAvatar::setJointRotation(int index, const glm::quat& rotation) { + if (QThread::currentThread() == thread()) { + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); + } +} + +void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) { + if (QThread::currentThread() == thread()) { + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); } } void MyAvatar::clearJointData(int index) { if (QThread::currentThread() == thread()) { // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, false, glm::quat(), 0.0f); + _rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f); _rig->clearJointAnimationPriority(index); } } @@ -1396,7 +1420,10 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { AnimPose pose = _debugDrawSkeleton->getRelativeBindPose(i); glm::quat jointRot; _rig->getJointRotationInConstrainedFrame(i, jointRot); + glm::vec3 jointTrans; + _rig->getJointTranslation(i, jointTrans); pose.rot = pose.rot * jointRot; + pose.trans = jointTrans; poses.push_back(pose); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c1a6ada751..6ee42778c8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -139,7 +139,10 @@ public: void clearLookAtTargetAvatar(); virtual void setJointRotations(QVector jointRotations); - virtual void setJointData(int index, const glm::quat& rotation); + virtual void setJointTranslations(QVector jointTranslations); + virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); + virtual void setJointRotation(int index, const glm::quat& rotation); + virtual void setJointTranslation(int index, const glm::vec3& translation); virtual void clearJointData(int index); virtual void clearJointsData(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 703f3983ee..9cffe5956f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -648,6 +648,7 @@ void SkeletonModel::computeBoundingShape() { // RECOVER FROM BOUNINDG SHAPE HACK: now that we're all done, restore the default pose for (int i = 0; i < numStates; i++) { _rig->restoreJointRotation(i, 1.0f, 1.0f); + _rig->restoreJointTranslation(i, 1.0f, 1.0f); } } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 23aa884933..f33c958fa8 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -147,8 +147,14 @@ void AnimClip::copyFromNetworkAnim() { for (int j = 0; j < animJointCount; j++) { int k = jointMap[j]; if (k >= 0 && k < skeletonJointCount) { - // currently FBX animations only have rotation. _anim[i][k].rot = _skeleton->getRelativeBindPose(k).rot * geom.animationFrames[i].rotations[j]; + + // TODO -- why does applying all the joint translations make a mutant? + if (animJoints[j].parentIndex == -1) { + _anim[i][k].trans = geom.animationFrames[i].translations[j] * extractScale(geom.offset); + } else { + _anim[i][k].trans = _skeleton->getRelativeBindPose(k).trans; + } } } } diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index de226092f1..037db8747c 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -322,9 +322,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar std::map::iterator constraintItr = _constraints.begin(); while (constraintItr != _constraints.end()) { int index = constraintItr->first; + glm::quat rotation = _relativePoses[index].rot; constraintItr->second->apply(rotation); _relativePoses[index].rot = rotation; + ++constraintItr; } } else { @@ -354,6 +356,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars } else { _relativePoses[i].rot = underPoses[i].rot; } + _relativePoses[i].trans = underPoses[i].trans; } } return evaluate(animVars, dt, triggersOut); diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index 8b9089f450..6ab4017aa3 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -55,6 +55,7 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, floa defaultRelPose = underPoses[jointVar.jointIndex]; defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); + defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); @@ -68,6 +69,7 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, floa defaultRelPose = AnimPose::identity; defaultAbsPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex); defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); + defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose // here we use the bind pose diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp index f2d12b398e..a6ac9c1f97 100644 --- a/libraries/animation/src/AnimationHandle.cpp +++ b/libraries/animation/src/AnimationHandle.cpp @@ -173,6 +173,7 @@ void AnimationHandle::applyFrame(float frameIndex) { const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(frameIndex) % frameCount); const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(frameIndex) % frameCount); float frameFraction = glm::fract(frameIndex); + for (int i = 0; i < _jointMappings.size(); i++) { int mapping = _jointMappings.at(i); if (mapping != -1) { // allow missing bones @@ -183,6 +184,15 @@ void AnimationHandle::applyFrame(float frameIndex) { _priority, false, _mix); + + // This isn't working. + // glm::vec3 floorTranslationPart = floorFrame.translations.at(i) * (1.0f - frameFraction); + // glm::vec3 ceilTranslationPart = ceilFrame.translations.at(i) * frameFraction; + // glm::vec3 floorCeilFraction = floorTranslationPart + ceilTranslationPart; + // glm::vec3 defaultTrans = _rig->getJointDefaultTranslationInConstrainedFrame(i); + // glm::vec3 mixedTranslation = floorCeilFraction * (1.0f - _mix) + defaultTrans * _mix; + // _rig->setJointTranslation(mapping, true, mixedTranslation, _priority); + } } } @@ -203,6 +213,7 @@ void AnimationHandle::restoreJoints() { int mapping = _jointMappings.at(i); if (mapping != -1) { _rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping)); + _rig->restoreJointTranslation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping)); } } } diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 0727d0956d..4dbf5207b1 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -20,9 +20,7 @@ void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) { // compute model transforms if (index == _rootJointIndex) { - // we always zero-out the translation part of an avatar's root join-transform. state.computeTransform(rootTransform); - clearJointTransformTranslation(index); } else { // guard against out-of-bounds access to _jointStates int parentIndex = state.getParentIndex(); @@ -97,3 +95,28 @@ void AvatarRig::setHandPosition(int jointIndex, shoulderRotation, priority); setJointRotationInBindFrame(jointIndex, rotation, priority); } + +void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setTranslation(translation, priority); + } else { + state.restoreTranslation(1.0f, priority); + } + } +} + + +void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + state.setTranslation(translation, priority); + } else { + state.restoreRotation(1.0f, priority); + state.restoreTranslation(1.0f, priority); + } + } +} diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h index f137e89939..f45c2951e9 100644 --- a/libraries/animation/src/AvatarRig.h +++ b/libraries/animation/src/AvatarRig.h @@ -24,6 +24,10 @@ class AvatarRig : public Rig { virtual void updateJointState(int index, glm::mat4 rootTransform); virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority); + virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority); + virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); }; + + #endif // hifi_AvatarRig_h diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index e65407116b..8748214734 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -27,3 +27,17 @@ void EntityRig::updateJointState(int index, glm::mat4 rootTransform) { } } } + + +void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + // state.setTranslation(translation, priority); + } else { + state.restoreRotation(1.0f, priority); + // state.restoreTranslation(1.0f, priority); + } + } +} diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h index b36ffb9874..17486bad78 100644 --- a/libraries/animation/src/EntityRig.h +++ b/libraries/animation/src/EntityRig.h @@ -24,6 +24,7 @@ class EntityRig : public Rig { virtual void updateJointState(int index, glm::mat4 rootTransform); virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) {} + virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); }; #endif // hifi_EntityRig_h diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp index 9597a46726..935319b489 100644 --- a/libraries/animation/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -43,6 +43,7 @@ void JointState::copyState(const JointState& other) { _isFree = other._isFree; _parentIndex = other._parentIndex; _defaultRotation = other._defaultRotation; + _defaultTranslation = other._defaultTranslation; _inverseDefaultRotation = other._inverseDefaultRotation; _translation = other._translation; _rotationMin = other._rotationMin; @@ -60,6 +61,7 @@ JointState::JointState(const FBXJoint& joint) { _parentIndex = joint.parentIndex; _translation = joint.translation; _defaultRotation = joint.rotation; + _defaultTranslation = _translation; _inverseDefaultRotation = joint.inverseDefaultRotation; _rotationMin = joint.rotationMin; _rotationMax = joint.rotationMax; @@ -92,6 +94,9 @@ glm::quat JointState::getRotation() const { } void JointState::initTransform(const glm::mat4& parentTransform) { + + _unitsScale = extractScale(parentTransform); + computeTransform(parentTransform); _positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform)); _distanceToParent = glm::length(_positionInParentFrame); @@ -101,11 +106,11 @@ void JointState::computeTransform(const glm::mat4& parentTransform, bool parentT if (!parentTransformChanged && !_transformChanged) { return; } - + glm::quat rotationInParentFrame = _preRotation * _rotationInConstrainedFrame * _postRotation; glm::mat4 transformInParentFrame = _preTransform * glm::mat4_cast(rotationInParentFrame) * _postTransform; glm::mat4 newTransform = parentTransform * glm::translate(_translation) * transformInParentFrame; - + if (newTransform != _transform) { _transform = newTransform; _transformChanged = true; @@ -139,6 +144,13 @@ void JointState::restoreRotation(float fraction, float priority) { } } +void JointState::restoreTranslation(float fraction, float priority) { + if (priority == _animationPriority || _animationPriority == 0.0f) { + _translation = _translation * (1.0f - fraction) + _defaultTranslation * fraction; + _animationPriority = 0.0f; + } +} + void JointState::setRotationInBindFrame(const glm::quat& rotation, float priority, bool constrain) { // rotation is from bind- to model-frame if (priority >= _animationPriority) { @@ -245,6 +257,14 @@ void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float p } } +void JointState::setTranslation(const glm::vec3& translation, float priority) { + if (priority >= _animationPriority || _animationPriority == 0.0f) { + _translation = translation / _unitsScale; + _transformChanged = true; + _animationPriority = priority; + } +} + void JointState::setRotationInConstrainedFrameInternal(const glm::quat& targetRotation) { if (_rotationInConstrainedFrame != targetRotation) { glm::quat parentRotation = computeParentRotation(); @@ -269,13 +289,17 @@ bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) c glm::abs(rotation.w - defaultRotation.w) < tolerance; } +bool JointState::translationIsDefault(const glm::vec3& translation, float tolerance) const { + return glm::distance(_defaultTranslation, translation * _unitsScale) < tolerance; +} + glm::quat JointState::getDefaultRotationInParentFrame() const { // NOTE: the result is constant and could be cached return _preRotation * _defaultRotation * _postRotation; } -const glm::vec3& JointState::getDefaultTranslationInConstrainedFrame() const { - return _translation; +glm::vec3 JointState::getDefaultTranslationInConstrainedFrame() const { + return _defaultTranslation * _unitsScale; } void JointState::slaveVisibleTransform() { diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h index 07ed010104..d11f98cdd6 100644 --- a/libraries/animation/src/JointState.h +++ b/libraries/animation/src/JointState.h @@ -77,6 +77,8 @@ public: /// \param priority priority level of this animation blend void restoreRotation(float fraction, float priority); + void restoreTranslation(float fraction, float priority); + /// \param rotation is from bind- to model-frame /// computes and sets new _rotationInConstrainedFrame /// NOTE: the JointState's model-frame transform/rotation are NOT updated! @@ -88,14 +90,18 @@ public: void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority, bool constrain); void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, bool constrain = false, float mix = 1.0f); + + void setTranslation(const glm::vec3& translation, float priority); + void setVisibleRotationInConstrainedFrame(const glm::quat& targetRotation); const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } const glm::quat& getVisibleRotationInConstrainedFrame() const { return _visibleRotationInConstrainedFrame; } bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const; + bool translationIsDefault(const glm::vec3& translation, float tolerance = EPSILON) const; glm::quat getDefaultRotationInParentFrame() const; - const glm::vec3& getDefaultTranslationInConstrainedFrame() const; + glm::vec3 getDefaultTranslationInConstrainedFrame() const; void clearTransformTranslation(); @@ -110,12 +116,13 @@ public: void setTransform(const glm::mat4& transform) { _transform = transform; } void setVisibleTransform(const glm::mat4& transform) { _visibleTransform = transform; } - const glm::vec3& getTranslation() const { return _translation; } + glm::vec3 getTranslation() const { return _translation * _unitsScale; } const glm::mat4& getPreTransform() const { return _preTransform; } const glm::mat4& getPostTransform() const { return _postTransform; } const glm::quat& getPreRotation() const { return _preRotation; } const glm::quat& getPostRotation() const { return _postRotation; } const glm::quat& getDefaultRotation() const { return _defaultRotation; } + glm::vec3 getDefaultTranslation() const { return _defaultTranslation * _unitsScale; } const glm::quat& getInverseDefaultRotation() const { return _inverseDefaultRotation; } const QString& getName() const { return _name; } bool getIsFree() const { return _isFree; } @@ -144,6 +151,7 @@ private: glm::quat _defaultRotation; // Not necessarilly bind rotation. See FBXJoint transform/bindTransform glm::quat _inverseDefaultRotation; + glm::vec3 _defaultTranslation; glm::vec3 _translation; QString _name; int _parentIndex; @@ -155,6 +163,8 @@ private: glm::mat4 _preTransform; glm::mat4 _postTransform; glm::quat _inverseBindRotation; + + glm::vec3 _unitsScale{1.0f, 1.0f, 1.0f}; }; #endif // hifi_JointState_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 1874939b3d..eaad5e6bb6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -270,6 +270,7 @@ void Rig::reset(const QVector& fbxJoints) { } for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f); + _jointStates[i].setTranslation(fbxJoints.at(i).translation, 0.0f); } } @@ -289,6 +290,16 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { return !state.rotationIsDefault(rotation); } +bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + translation = state.getTranslation(); + return !state.translationIsDefault(translation); +} + + bool Rig::getVisibleJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; @@ -302,6 +313,7 @@ void Rig::clearJointState(int index) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; state.setRotationInConstrainedFrame(glm::quat(), 0.0f); + state.setTranslation(state.getDefaultTranslationInConstrainedFrame(), 0.0f); } } @@ -328,7 +340,7 @@ void Rig::setJointAnimatinoPriority(int index, float newPriority) { } } -void Rig::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { +void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; if (valid) { @@ -345,6 +357,12 @@ void Rig::restoreJointRotation(int index, float fraction, float priority) { } } +void Rig::restoreJointTranslation(int index, float fraction, float priority) { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index].restoreTranslation(fraction, priority); + } +} + bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { @@ -380,6 +398,14 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { return true; } +bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + translation = _jointStates[jointIndex].getTranslation(); + return true; +} + bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; @@ -590,7 +616,13 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { // copy poses into jointStates const float PRIORITY = 1.0f; for (size_t i = 0; i < poses.size(); i++) { - setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false); + setJointRotationInConstrainedFrame((int)i, + glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, + PRIORITY, + false); + + JointState& state = _jointStates[i]; + setJointTranslation((int)i, true, poses[i].trans, PRIORITY); } } else { @@ -866,6 +898,7 @@ bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, c foreach (int index, freeLineage) { JointState& state = _jointStates[index]; state.restoreRotation(fraction, priority); + state.restoreTranslation(fraction, priority); } return true; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 90082ca38b..f31d030910 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -123,12 +123,13 @@ public: int rightShoulderJointIndex); bool jointStatesEmpty() { return _jointStates.isEmpty(); }; int getJointStateCount() const { return _jointStates.size(); } - int indexOfJoint(const QString& jointName) ; + int indexOfJoint(const QString& jointName); void initJointTransforms(glm::mat4 rootTransform); void clearJointTransformTranslation(int jointIndex); void reset(const QVector& fbxJoints); bool getJointStateRotation(int index, glm::quat& rotation) const; + bool getJointStateTranslation(int index, glm::vec3& translation) const; void applyJointRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority); JointState getJointState(int jointIndex) const; // XXX bool getVisibleJointState(int index, glm::quat& rotation) const; @@ -137,14 +138,21 @@ public: void clearJointAnimationPriority(int index); float getJointAnimatinoPriority(int index); void setJointAnimatinoPriority(int index, float newPriority); - void setJointState(int index, bool valid, const glm::quat& rotation, float priority); + + virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, + float priority) = 0; + virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {} + void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority); + void restoreJointRotation(int index, float fraction, float priority); + void restoreJointTranslation(int index, float fraction, float priority); bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const; bool getJointPosition(int jointIndex, glm::vec3& position) const; bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const; bool getJointRotation(int jointIndex, glm::quat& rotation) const; + bool getJointTranslation(int jointIndex, glm::vec3& translation) const; bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 104b592c1a..3ef53e245b 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -49,6 +49,7 @@ AvatarData::AvatarData() : _keyState(NO_KEY_DOWN), _forceFaceTrackerConnected(false), _hasNewJointRotations(true), + _hasNewJointTranslations(true), _headData(NULL), _handData(NULL), _faceModelURL("http://invalid.com"), @@ -278,12 +279,16 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { // pupil dilation destinationBuffer += packFloatToByte(destinationBuffer, _headData->_pupilDilation, 1.0f); - // joint data + // joint rotation data *destinationBuffer++ = _jointData.size(); unsigned char* validityPosition = destinationBuffer; unsigned char validity = 0; int validityBit = 0; + #ifdef WANT_DEBUG + int rotationSentCount = 0; + #endif + _lastSentJointData.resize(_jointData.size()); for (int i=0; i < _jointData.size(); i++) { @@ -292,7 +297,12 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { if (sendAll || !cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { - validity |= (1 << validityBit); + if (data.rotationSet) { + validity |= (1 << validityBit); + #ifdef WANT_DEBUG + rotationSentCount++; + #endif + } } } if (++validityBit == BITS_IN_BYTE) { @@ -317,6 +327,73 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { } } + + // joint translation data + validityPosition = destinationBuffer; + validity = 0; + validityBit = 0; + + #ifdef WANT_DEBUG + int translationSentCount = 0; + #endif + + float maxTranslationDimension = 0.0; + for (int i=0; i < _jointData.size(); i++) { + const JointData& data = _jointData.at(i); + if (sendAll || _lastSentJointData[i].translation != data.translation) { + if (sendAll || + !cullSmallChanges || + glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) { + if (data.translationSet) { + validity |= (1 << validityBit); + #ifdef WANT_DEBUG + translationSentCount++; + #endif + maxTranslationDimension = glm::max(fabsf(data.translation.x), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.y), maxTranslationDimension); + maxTranslationDimension = glm::max(fabsf(data.translation.z), maxTranslationDimension); + } + } + } + if (++validityBit == BITS_IN_BYTE) { + *destinationBuffer++ = validity; + validityBit = validity = 0; + } + } + + + if (validityBit != 0) { + *destinationBuffer++ = validity; + } + + // TODO -- automatically pick translationCompressionRadix + int translationCompressionRadix = 12; + + *destinationBuffer++ = translationCompressionRadix; + + validityBit = 0; + validity = *validityPosition++; + for (int i = 0; i < _jointData.size(); i ++) { + const JointData& data = _jointData[ i ]; + if (validity & (1 << validityBit)) { + destinationBuffer += + packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, translationCompressionRadix); + } + if (++validityBit == BITS_IN_BYTE) { + validityBit = 0; + validity = *validityPosition++; + } + } + + #ifdef WANT_DEBUG + if (sendAll) { + qDebug() << "SENDING -- rotations:" << rotationSentCount << "translations:" << translationSentCount + << "largest:" << maxTranslationDimension + << "radix:" << translationCompressionRadix + << "size:" << (int)(destinationBuffer - startPosition); + } + #endif + return avatarDataByteArray.left(destinationBuffer - startPosition); } @@ -328,7 +405,16 @@ void AvatarData::doneEncoding(bool cullSmallChanges) { if (_lastSentJointData[i].rotation != data.rotation) { if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { - _lastSentJointData[i].rotation = data.rotation; + if (data.rotationSet) { + _lastSentJointData[i].rotation = data.rotation; + } + } + + if (!cullSmallChanges || + glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) { + if (data.translationSet) { + _lastSentJointData[i].translation = data.translation; + } } } } @@ -374,7 +460,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // + 1 byte for numJoints (0) // = 39 bytes int minPossibleSize = 39; - + int maxAvailableSize = buffer.size(); if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { @@ -556,7 +642,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); } // 1 byte - // joint data + + //----------------------- + // joint rotations + //----------------------- + int numJoints = *sourceBuffer++; int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE); minPossibleSize += bytesOfValidity; @@ -569,13 +659,13 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } return maxAvailableSize; } - int numValidJoints = 0; + int numValidJointRotations = 0; _jointData.resize(numJoints); - QVector valids; - valids.resize(numJoints); + QVector validRotations; + validRotations.resize(numJoints); - { // validity bits + { // rotation validity bits unsigned char validity = 0; int validityBit = 0; for (int i = 0; i < numJoints; i++) { @@ -584,20 +674,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } bool valid = (bool)(validity & (1 << validityBit)); if (valid) { - ++numValidJoints; + ++numValidJointRotations; } - valids[i] = valid; + validRotations[i] = valid; validityBit = (validityBit + 1) % BITS_IN_BYTE; } - } - // 1 + bytesOfValidity bytes + } // 1 + bytesOfValidity bytes // each joint rotation component is stored in two bytes (sizeof(uint16_t)) int COMPONENTS_PER_QUATERNION = 4; - minPossibleSize += numValidJoints * COMPONENTS_PER_QUATERNION * sizeof(uint16_t); + minPossibleSize += numValidJointRotations * COMPONENTS_PER_QUATERNION * sizeof(uint16_t); if (minPossibleSize > maxAvailableSize) { if (shouldLogError(now)) { - qCDebug(avatars) << "Malformed AvatarData packet after JointData;" + qCDebug(avatars) << "Malformed AvatarData packet after JointData rotation validity;" << " displayName = '" << _displayName << "'" << " minPossibleSize = " << minPossibleSize << " maxAvailableSize = " << maxAvailableSize; @@ -608,13 +697,75 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { { // joint data for (int i = 0; i < numJoints; i++) { JointData& data = _jointData[i]; - if (valids[i]) { + if (validRotations[i]) { _hasNewJointRotations = true; + data.rotationSet = true; sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); } } } // numJoints * 8 bytes + + //----------------------- + // joint translations + //----------------------- + + // get translation validity bits -- these indicate which translations were packed + int numValidJointTranslations = 0; + QVector validTranslations; + validTranslations.resize(numJoints); + + { // translation validity bits + unsigned char validity = 0; + int validityBit = 0; + for (int i = 0; i < numJoints; i++) { + if (validityBit == 0) { + validity = *sourceBuffer++; + } + bool valid = (bool)(validity & (1 << validityBit)); + if (valid) { + ++numValidJointTranslations; + } + validTranslations[i] = valid; + validityBit = (validityBit + 1) % BITS_IN_BYTE; + } + } // 1 + bytesOfValidity bytes + + // each joint translation component is stored in 6 bytes. 1 byte for translationCompressionRadix + minPossibleSize += numValidJointTranslations * 6 + 1; + if (minPossibleSize > maxAvailableSize) { + if (shouldLogError(now)) { + qCDebug(avatars) << "Malformed AvatarData packet after JointData translation validity;" + << " displayName = '" << _displayName << "'" + << " minPossibleSize = " << minPossibleSize + << " maxAvailableSize = " << maxAvailableSize; + } + return maxAvailableSize; + } + + int translationCompressionRadix = *sourceBuffer++; + + { // joint data + for (int i = 0; i < numJoints; i++) { + JointData& data = _jointData[i]; + if (validTranslations[i]) { + sourceBuffer += + unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, translationCompressionRadix); + _hasNewJointTranslations = true; + data.translationSet = true; + } + } + } // numJoints * 12 bytes + + #ifdef WANT_DEBUG + if (numValidJointRotations > 15) { + qDebug() << "RECEIVING -- rotations:" << numValidJointRotations + << "translations:" << numValidJointTranslations + << "radix:" << translationCompressionRadix + << "size:" << (int)(sourceBuffer - startPosition); + } + #endif + int numBytesRead = sourceBuffer - startPosition; _averageBytesReceived.updateAverage(numBytesRead); return numBytesRead; @@ -800,7 +951,7 @@ void AvatarData::changeReferential(Referential* ref) { _referential = ref; } -void AvatarData::setJointData(int index, const glm::quat& rotation) { +void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { if (index == -1) { return; } @@ -813,6 +964,7 @@ void AvatarData::setJointData(int index, const glm::quat& rotation) { } JointData& data = _jointData[index]; data.rotation = rotation; + data.translation = translation; } void AvatarData::clearJointData(int index) { @@ -854,13 +1006,67 @@ glm::quat AvatarData::getJointRotation(int index) const { return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat(); } -void AvatarData::setJointData(const QString& name, const glm::quat& rotation) { + +glm::vec3 AvatarData::getJointTranslation(int index) const { + if (index == -1) { + return glm::vec3(); + } + if (QThread::currentThread() != thread()) { + glm::vec3 result; + QMetaObject::invokeMethod(const_cast(this), "getJointTranslation", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index)); + return result; + } + return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3(); +} + +glm::vec3 AvatarData::getJointTranslation(const QString& name) const { + if (QThread::currentThread() != thread()) { + glm::vec3 result; + QMetaObject::invokeMethod(const_cast(this), "getJointTranslation", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(glm::vec3, result), Q_ARG(const QString&, name)); + return result; + } + return getJointTranslation(getJointIndex(name)); +} + +void AvatarData::setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), Q_ARG(const glm::quat&, rotation)); return; } - setJointData(getJointIndex(name), rotation); + setJointData(getJointIndex(name), rotation, translation); +} + +void AvatarData::setJointRotation(int index, const glm::quat& rotation) { + if (index == -1) { + return; + } + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation)); + return; + } + if (_jointData.size() <= index) { + _jointData.resize(index + 1); + } + JointData& data = _jointData[index]; + data.rotation = rotation; +} + +void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { + if (index == -1) { + return; + } + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation)); + return; + } + if (_jointData.size() <= index) { + _jointData.resize(index + 1); + } + JointData& data = _jointData[index]; + data.translation = translation; } void AvatarData::clearJointData(const QString& name) { @@ -918,7 +1124,25 @@ void AvatarData::setJointRotations(QVector jointRotations) { } for (int i = 0; i < jointRotations.size(); ++i) { if (i < _jointData.size()) { - setJointData(i, jointRotations[i]); + setJointRotation(i, jointRotations[i]); + } + } +} + +void AvatarData::setJointTranslations(QVector jointTranslations) { + if (QThread::currentThread() != thread()) { + QVector result; + QMetaObject::invokeMethod(const_cast(this), + "setJointTranslations", Qt::BlockingQueuedConnection, + Q_ARG(QVector, jointTranslations)); + } + + if (_jointData.size() < jointTranslations.size()) { + _jointData.resize(jointTranslations.size()); + } + for (int i = 0; i < jointTranslations.size(); ++i) { + if (i < _jointData.size()) { + setJointTranslation(i, jointTranslations[i]); } } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a934b98037..e4022fd474 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -117,6 +117,7 @@ const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02f; // this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer const float AVATAR_MIN_ROTATION_DOT = 0.9999999f; +const float AVATAR_MIN_TRANSLATION = 0.0001f; // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found). @@ -240,20 +241,24 @@ public: Q_INVOKABLE char getHandState() const { return _handState; } const QVector& getJointData() const { return _jointData; } - void setJointData(const QVector& jointData) { _jointData = jointData; } - Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation); + Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); + Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); + Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation); Q_INVOKABLE virtual void clearJointData(int index); Q_INVOKABLE bool isJointDataValid(int index) const; Q_INVOKABLE virtual glm::quat getJointRotation(int index) const; + Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; - Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation); + Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); Q_INVOKABLE void clearJointData(const QString& name); Q_INVOKABLE bool isJointDataValid(const QString& name) const; Q_INVOKABLE glm::quat getJointRotation(const QString& name) const; + Q_INVOKABLE glm::vec3 getJointTranslation(const QString& name) const; Q_INVOKABLE virtual QVector getJointRotations() const; Q_INVOKABLE virtual void setJointRotations(QVector jointRotations); + Q_INVOKABLE virtual void setJointTranslations(QVector jointTranslations); Q_INVOKABLE virtual void clearJointsData(); @@ -387,6 +392,7 @@ protected: bool _forceFaceTrackerConnected; bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar + bool _hasNewJointTranslations; // set in AvatarData, cleared in Avatar HeadData* _headData; HandData* _handData; @@ -435,6 +441,9 @@ Q_DECLARE_METATYPE(AvatarData*) class JointData { public: glm::quat rotation; + bool rotationSet = false; + glm::vec3 translation; + bool translationSet = false; }; class AttachmentData { diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 29544924b2..a425323a41 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -254,8 +254,17 @@ void Player::play() { nextFrame.getJointRotations()[i], _frameInterpolationFactor); } + + QVector jointTranslations(currentFrame.getJointTranslations().size()); + for (int i = 0; i < currentFrame.getJointTranslations().size(); ++i) { + jointTranslations[i] = + currentFrame.getJointTranslations()[i] * (1.0f - _frameInterpolationFactor) + + nextFrame.getJointTranslations()[i] * _frameInterpolationFactor; + } + _avatar->setJointRotations(jointRotations); - + _avatar->setJointTranslations(jointTranslations); + HeadData* head = const_cast(_avatar->getHeadData()); if (head) { // Make sure fake face tracker connection doesn't get turned off diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index 76e1d68050..2c87485d01 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -239,6 +239,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { if (i == 0 || frame._jointRotations[j] != previousFrame._jointRotations[j]) { writeQuat(stream, frame._jointRotations[j]); + // XXX handle translations mask.setBit(maskIndex); } maskIndex++; @@ -561,7 +562,10 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString frame._jointRotations[j] = previousFrame._jointRotations[j]; } } - + + // XXX handle translations + + if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) { frame._translation = previousFrame._translation; } @@ -670,7 +674,9 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr for (int i = 0; i < jointRotationSize; ++i) { fileStream >> baseFrame._jointRotations[i].x >> baseFrame._jointRotations[i].y >> baseFrame._jointRotations[i].z >> baseFrame._jointRotations[i].w; } - + + // XXX handle translations + fileStream >> baseFrame._translation.x >> baseFrame._translation.y >> baseFrame._translation.z; fileStream >> baseFrame._rotation.x >> baseFrame._rotation.y >> baseFrame._rotation.z >> baseFrame._rotation.w; fileStream >> baseFrame._scale; @@ -736,7 +742,9 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr frame._jointRotations[i] = previousFrame._jointRotations[i]; } } - + + // XXX handle translations + if (mask[maskIndex++]) { stream >> frame._translation.x >> frame._translation.y >> frame._translation.z; frame._translation = context.orientationInv * frame._translation; diff --git a/libraries/avatars/src/Recording.h b/libraries/avatars/src/Recording.h index 49d12ec5b5..3533af6535 100644 --- a/libraries/avatars/src/Recording.h +++ b/libraries/avatars/src/Recording.h @@ -83,6 +83,7 @@ class RecordingFrame { public: QVector getBlendshapeCoefficients() const { return _blendshapeCoefficients; } QVector getJointRotations() const { return _jointRotations; } + QVector getJointTranslations() const { return _jointTranslations; } glm::vec3 getTranslation() const { return _translation; } glm::quat getRotation() const { return _rotation; } float getScale() const { return _scale; } @@ -94,6 +95,7 @@ public: protected: void setBlendshapeCoefficients(QVector blendshapeCoefficients); void setJointRotations(QVector jointRotations) { _jointRotations = jointRotations; } + void setJointTranslations(QVector jointTranslations) { _jointTranslations = jointTranslations; } void setTranslation(const glm::vec3& translation) { _translation = translation; } void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setScale(float scale) { _scale = scale; } @@ -105,6 +107,7 @@ protected: private: QVector _blendshapeCoefficients; QVector _jointRotations; + QVector _jointTranslations; glm::vec3 _translation; glm::quat _rotation; float _scale; @@ -124,4 +127,4 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename); RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString& filename); RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QString& filename, const QByteArray& byteArray); -#endif // hifi_Recording_h \ No newline at end of file +#endif // hifi_Recording_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 74317b7eff..6d7399b07a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -276,10 +276,13 @@ void RenderableModelEntityItem::render(RenderArgs* args) { if (jointsMapped()) { bool newFrame; - auto frameData = getAnimationFrame(newFrame); + QVector frameDataRotations; + QVector frameDataTranslations; + getAnimationFrame(newFrame, frameDataRotations, frameDataTranslations); + assert(frameDataRotations.size() == frameDataTranslations.size()); if (newFrame) { - for (int i = 0; i < frameData.size(); i++) { - _model->setJointState(i, true, frameData[i]); + for (int i = 0; i < frameDataRotations.size(); i++) { + _model->setJointState(i, true, frameDataRotations[i], frameDataTranslations[i], 1.0f); } } } diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 70747937d8..44b4aa99bf 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -218,18 +218,20 @@ void ModelEntityItem::mapJoints(const QStringList& modelJointNames) { } } -const QVector& ModelEntityItem::getAnimationFrame(bool& newFrame) { +void ModelEntityItem::getAnimationFrame(bool& newFrame, + QVector& rotationsResult, QVector& translationsResult) { newFrame = false; if (!hasAnimation() || !_jointMappingCompleted) { - return _lastKnownFrameData; + rotationsResult = _lastKnownFrameDataRotations; + translationsResult = _lastKnownFrameDataTranslations; } - + AnimationPointer myAnimation = getAnimation(_animationURL); // FIXME: this could be optimized if (myAnimation && myAnimation->isLoaded()) { - + const QVector& frames = myAnimation->getFramesReference(); // NOTE: getFrames() is too heavy - + int frameCount = frames.size(); if (frameCount > 0) { int animationFrameIndex = (int)(glm::floor(getAnimationFrameIndex())) % frameCount; @@ -240,20 +242,27 @@ const QVector& ModelEntityItem::getAnimationFrame(bool& newFrame) { if (animationFrameIndex != _lastKnownFrameIndex) { _lastKnownFrameIndex = animationFrameIndex; newFrame = true; - - const QVector& rotations = frames[animationFrameIndex].rotations; - _lastKnownFrameData.resize(_jointMapping.size()); + const QVector& rotations = frames[animationFrameIndex].rotations; + const QVector& translations = frames[animationFrameIndex].translations; + + _lastKnownFrameDataRotations.resize(_jointMapping.size()); + _lastKnownFrameDataTranslations.resize(_jointMapping.size()); for (int j = 0; j < _jointMapping.size(); j++) { - int rotationIndex = _jointMapping[j]; - if (rotationIndex != -1 && rotationIndex < rotations.size()) { - _lastKnownFrameData[j] = rotations[rotationIndex]; + int index = _jointMapping[j]; + if (index != -1 && index < rotations.size()) { + _lastKnownFrameDataRotations[j] = rotations[index]; + } + if (index != -1 && index < translations.size()) { + _lastKnownFrameDataTranslations[j] = translations[index]; } } } } } - return _lastKnownFrameData; + + rotationsResult = _lastKnownFrameDataRotations; + translationsResult = _lastKnownFrameDataTranslations; } bool ModelEntityItem::isAnimatingSomething() const { diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index bf6d7a9785..8bf6658bf3 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -106,7 +106,7 @@ public: float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); } void mapJoints(const QStringList& modelJointNames); - const QVector& getAnimationFrame(bool& newFrame); + void getAnimationFrame(bool& newFrame, QVector& rotationsResult, QVector& translationsResult); bool jointsMapped() const { return _jointMappingCompleted; } bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); } @@ -123,7 +123,8 @@ public: static void cleanupLoadedAnimations(); protected: - QVector _lastKnownFrameData; + QVector _lastKnownFrameDataRotations; + QVector _lastKnownFrameDataTranslations; int _lastKnownFrameIndex; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index da240e826a..1accbf4238 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -530,6 +530,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QHash typeFlags; QHash localRotations; + QHash localTranslations; QHash xComponents; QHash yComponents; QHash zComponents; @@ -1104,16 +1105,16 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS normalTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("specular") || type.contains("reflection")) { specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - + } else if (type == "lcl rotation") { localRotations.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - + } else if (type == "lcl translation") { + localTranslations.insert(getID(connection.properties, 2), getID(connection.properties, 1)); + } else if (type == "d|x") { xComponents.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - } else if (type == "d|y") { yComponents.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - } else if (type == "d|z") { zComponents.insert(getID(connection.properties, 2), getID(connection.properties, 1)); @@ -1224,6 +1225,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS for (int i = 0; i < frameCount; i++) { FBXAnimationFrame frame; frame.rotations.resize(modelIDs.size()); + frame.translations.resize(modelIDs.size()); geometry.animationFrames.append(frame); } @@ -1247,7 +1249,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS joint.freeLineage.append(index); } joint.freeLineage.remove(lastFreeIndex + 1, joint.freeLineage.size() - lastFreeIndex - 1); - joint.translation = model.translation; + joint.translation = model.translation; // these are usually in centimeters joint.preTransform = model.preTransform; joint.preRotation = model.preRotation; joint.rotation = model.rotation; @@ -1272,7 +1274,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = model.name; - + foreach (const QString& childID, _connectionChildMap.values(modelID)) { QString type = typeFlags.value(childID); if (!type.isEmpty()) { @@ -1285,17 +1287,28 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.joints.append(joint); geometry.jointIndices.insert(model.name, geometry.joints.size()); - + QString rotationID = localRotations.value(modelID); - AnimationCurve xCurve = animationCurves.value(xComponents.value(rotationID)); - AnimationCurve yCurve = animationCurves.value(yComponents.value(rotationID)); - AnimationCurve zCurve = animationCurves.value(zComponents.value(rotationID)); + AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID)); + AnimationCurve yRotCurve = animationCurves.value(yComponents.value(rotationID)); + AnimationCurve zRotCurve = animationCurves.value(zComponents.value(rotationID)); + + QString translationID = localTranslations.value(modelID); + AnimationCurve xPosCurve = animationCurves.value(xComponents.value(translationID)); + AnimationCurve yPosCurve = animationCurves.value(yComponents.value(translationID)); + AnimationCurve zPosCurve = animationCurves.value(zComponents.value(translationID)); + glm::vec3 defaultValues = glm::degrees(safeEulerAngles(joint.rotation)); + for (int i = 0; i < frameCount; i++) { geometry.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( - xCurve.values.isEmpty() ? defaultValues.x : xCurve.values.at(i % xCurve.values.size()), - yCurve.values.isEmpty() ? defaultValues.y : yCurve.values.at(i % yCurve.values.size()), - zCurve.values.isEmpty() ? defaultValues.z : zCurve.values.at(i % zCurve.values.size())))); + xRotCurve.values.isEmpty() ? defaultValues.x : xRotCurve.values.at(i % xRotCurve.values.size()), + yRotCurve.values.isEmpty() ? defaultValues.y : yRotCurve.values.at(i % yRotCurve.values.size()), + zRotCurve.values.isEmpty() ? defaultValues.z : zRotCurve.values.at(i % zRotCurve.values.size())))); + geometry.animationFrames[i].translations[jointIndex] = glm::vec3( + xPosCurve.values.isEmpty() ? defaultValues.x : xPosCurve.values.at(i % xPosCurve.values.size()), + yPosCurve.values.isEmpty() ? defaultValues.y : yPosCurve.values.at(i % yPosCurve.values.size()), + zPosCurve.values.isEmpty() ? defaultValues.z : zPosCurve.values.at(i % zPosCurve.values.size())); } } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 5cfff9826f..20f8fffe44 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -208,8 +208,8 @@ public: /// A single animation frame extracted from an FBX document. class FBXAnimationFrame { public: - QVector rotations; + QVector translations; }; /// A light in an FBX document. diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index a8cf743686..ca75a86158 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -39,6 +39,9 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: return VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER; + case PacketType::AvatarData: + case PacketType::BulkAvatarData: + return 15; default: return 14; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7aee46b108..34896d5714 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1001,8 +1001,16 @@ void Model::clearJointState(int index) { _rig->clearJointState(index); } -void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) { - _rig->setJointState(index, valid, rotation, priority); +void Model::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { + _rig->setJointState(index, valid, rotation, translation, priority); +} + +void Model::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { + _rig->setJointRotation(index, valid, rotation, priority); +} + +void Model::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { + _rig->setJointTranslation(index, valid, translation, priority); } int Model::getParentJointIndex(int jointIndex) const { @@ -1074,6 +1082,10 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation) const { return _rig->getJointRotation(jointIndex, rotation); } +bool Model::getJointTranslation(int jointIndex, glm::vec3& translation) const { + return _rig->getJointTranslation(jointIndex, translation); +} + bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const { return _rig->getJointCombinedRotation(jointIndex, rotation, _rotation); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index de760dc793..460bc4f044 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -126,7 +126,9 @@ public: QStringList getJointNames() const; /// Sets the joint state at the specified index. - void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f); + void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); + void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority); + void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority); bool findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, QString& extraInfo, bool pickAgainstTriangles = false); @@ -160,6 +162,7 @@ public: /// \param rotation[out] rotation of joint in model-frame /// \return true if joint exists bool getJointRotation(int jointIndex, glm::quat& rotation) const; + bool getJointTranslation(int jointIndex, glm::vec3& translation) const; /// Returns the index of the parent of the indexed joint, or -1 if not found. int getParentJointIndex(int jointIndex) const; diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 7d56157e53..31a6096d13 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -33,6 +33,8 @@ const vec3& Vectors::RIGHT = Vectors::UNIT_X; const vec3& Vectors::UP = Vectors::UNIT_Y; const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z; +const quat Quaternions::ZERO{ 1.0f, 0.0f, 0.0f, 0.0f }; + // Safe version of glm::mix; based on the code in Nick Bobick's article, // http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, // https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 6683088306..e2833b46e3 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -53,6 +53,12 @@ const glm::vec3 IDENTITY_FRONT = glm::vec3( 0.0f, 0.0f,-1.0f); glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); + +class Quaternions { + public: + static const quat ZERO; +}; + class Vectors { public: static const vec3 UNIT_X; From 04eed64c7cdfd01853e1b5ff1f843e6a42fb8053 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 28 Sep 2015 15:26:32 -0700 Subject: [PATCH 02/10] fix bug in JointState::translationIsDefault --- libraries/animation/src/JointState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp index 935319b489..e838acf72a 100644 --- a/libraries/animation/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -290,7 +290,7 @@ bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) c } bool JointState::translationIsDefault(const glm::vec3& translation, float tolerance) const { - return glm::distance(_defaultTranslation, translation * _unitsScale) < tolerance; + return glm::distance(_defaultTranslation * _unitsScale, translation * _unitsScale) < tolerance; } glm::quat JointState::getDefaultRotationInParentFrame() const { From 874bded236c1f8fd083235b5a1622fb6176c3682 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 28 Sep 2015 18:14:12 -0700 Subject: [PATCH 03/10] remove horizontal line-break comments --- libraries/avatars/src/AvatarData.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3ef53e245b..b54ed7af6a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -642,11 +642,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { sourceBuffer += unpackFloatFromByte(sourceBuffer, _headData->_pupilDilation, 1.0f); } // 1 byte - - //----------------------- // joint rotations - //----------------------- - int numJoints = *sourceBuffer++; int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE); minPossibleSize += bytesOfValidity; @@ -705,11 +701,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } } // numJoints * 8 bytes - - //----------------------- // joint translations - //----------------------- - // get translation validity bits -- these indicate which translations were packed int numValidJointTranslations = 0; QVector validTranslations; From ddeed8b4eeb0e6acfcc4c12d12d4fbc53db7d887 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 29 Sep 2015 06:51:24 -0700 Subject: [PATCH 04/10] code review --- libraries/avatars/src/Recording.cpp | 9 ++++----- libraries/shared/src/GLMHelpers.cpp | 2 +- libraries/shared/src/GLMHelpers.h | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index 2c87485d01..5514b97b6f 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -239,7 +239,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { if (i == 0 || frame._jointRotations[j] != previousFrame._jointRotations[j]) { writeQuat(stream, frame._jointRotations[j]); - // XXX handle translations + // TODO -- handle translations mask.setBit(maskIndex); } maskIndex++; @@ -563,8 +563,7 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } } - // XXX handle translations - + // TODO -- handle translations if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) { frame._translation = previousFrame._translation; @@ -675,7 +674,7 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr fileStream >> baseFrame._jointRotations[i].x >> baseFrame._jointRotations[i].y >> baseFrame._jointRotations[i].z >> baseFrame._jointRotations[i].w; } - // XXX handle translations + // TODO -- handle translations fileStream >> baseFrame._translation.x >> baseFrame._translation.y >> baseFrame._translation.z; fileStream >> baseFrame._rotation.x >> baseFrame._rotation.y >> baseFrame._rotation.z >> baseFrame._rotation.w; @@ -743,7 +742,7 @@ RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QStr } } - // XXX handle translations + // TODO -- handle translations if (mask[maskIndex++]) { stream >> frame._translation.x >> frame._translation.y >> frame._translation.z; diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 31a6096d13..dc7887c9f8 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -33,7 +33,7 @@ const vec3& Vectors::RIGHT = Vectors::UNIT_X; const vec3& Vectors::UP = Vectors::UNIT_Y; const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z; -const quat Quaternions::ZERO{ 1.0f, 0.0f, 0.0f, 0.0f }; +const quat Quaternions::IDENTITY{ 1.0f, 0.0f, 0.0f, 0.0f }; // Safe version of glm::mix; based on the code in Nick Bobick's article, // http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index e2833b46e3..7bdd3bf2de 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -56,7 +56,7 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); class Quaternions { public: - static const quat ZERO; + static const quat IDENTITY; }; class Vectors { From 5e2279ed4bb6af9bc88ac467289cdd5bb677a078 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 29 Sep 2015 16:35:55 -0700 Subject: [PATCH 05/10] AnimManipulator: Don't read defaultAbsPose.trans anim var. At the moment the manipulator only supports rotations. --- libraries/animation/src/AnimManipulator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index 6ab4017aa3..8b9089f450 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -55,7 +55,6 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, floa defaultRelPose = underPoses[jointVar.jointIndex]; defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); - defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); @@ -69,7 +68,6 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, floa defaultRelPose = AnimPose::identity; defaultAbsPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex); defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); - defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose // here we use the bind pose From 8453043038fbdd3d7bc1e650fde4e60da334456f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 1 Oct 2015 13:23:51 -0700 Subject: [PATCH 06/10] remove unused variable --- libraries/animation/src/Rig.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9918b09b8c..a3c48fca5f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -625,7 +625,6 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, false, 1.0f); - JointState& state = _jointStates[i]; setJointTranslation((int)i, true, poses[i].trans, PRIORITY); } From 8bfd38ce7c6ce216a52acfc0b27fe9cb2a403251 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 1 Oct 2015 14:12:32 -0700 Subject: [PATCH 07/10] FBXReader: When the animation has no animation curve use the joint's default position. --- libraries/fbx/src/FBXReader.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 297502d7a1..0c738c5a5b 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1306,17 +1306,18 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS AnimationCurve yPosCurve = animationCurves.value(yComponents.value(translationID)); AnimationCurve zPosCurve = animationCurves.value(zComponents.value(translationID)); - glm::vec3 defaultValues = glm::degrees(safeEulerAngles(joint.rotation)); + glm::vec3 defaultRotValues = glm::degrees(safeEulerAngles(joint.rotation)); + glm::vec3 defaultPosValues = joint.translation; for (int i = 0; i < frameCount; i++) { geometry.animationFrames[i].rotations[jointIndex] = glm::quat(glm::radians(glm::vec3( - xRotCurve.values.isEmpty() ? defaultValues.x : xRotCurve.values.at(i % xRotCurve.values.size()), - yRotCurve.values.isEmpty() ? defaultValues.y : yRotCurve.values.at(i % yRotCurve.values.size()), - zRotCurve.values.isEmpty() ? defaultValues.z : zRotCurve.values.at(i % zRotCurve.values.size())))); + xRotCurve.values.isEmpty() ? defaultRotValues.x : xRotCurve.values.at(i % xRotCurve.values.size()), + yRotCurve.values.isEmpty() ? defaultRotValues.y : yRotCurve.values.at(i % yRotCurve.values.size()), + zRotCurve.values.isEmpty() ? defaultRotValues.z : zRotCurve.values.at(i % zRotCurve.values.size())))); geometry.animationFrames[i].translations[jointIndex] = glm::vec3( - xPosCurve.values.isEmpty() ? defaultValues.x : xPosCurve.values.at(i % xPosCurve.values.size()), - yPosCurve.values.isEmpty() ? defaultValues.y : yPosCurve.values.at(i % yPosCurve.values.size()), - zPosCurve.values.isEmpty() ? defaultValues.z : zPosCurve.values.at(i % zPosCurve.values.size())); + xPosCurve.values.isEmpty() ? defaultPosValues.x : xPosCurve.values.at(i % xPosCurve.values.size()), + yPosCurve.values.isEmpty() ? defaultPosValues.y : yPosCurve.values.at(i % yPosCurve.values.size()), + zPosCurve.values.isEmpty() ? defaultPosValues.z : zPosCurve.values.at(i % zPosCurve.values.size())); } } From fe5ea471a1bd6faefe6ff90c5f80388ae89a10b7 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 1 Oct 2015 16:10:33 -0700 Subject: [PATCH 08/10] AnimClip: read in translations from fbx file and pre-process them Do the following things to the translations 1. scale by the model offset, this should move the translations into the correct units (meters). 2. compute the ratio between the bone length in the animation and the skeleton. 3. subtract the anim translation from the first translation frame in the animation effectively turning it into a bind pose delta translation. 4. apply bone length ratio to the resulting delta. 5. set the final translation to be the skeleton rel bind pose + this scaled delta translation --- libraries/animation/src/AnimClip.cpp | 47 +++++++++++++++++++--------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index f33c958fa8..c432b3b9ac 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -135,29 +135,46 @@ void AnimClip::copyFromNetworkAnim() { const int frameCount = geom.animationFrames.size(); const int skeletonJointCount = _skeleton->getNumJoints(); _anim.resize(frameCount); - for (int i = 0; i < frameCount; i++) { + + const glm::vec3 offsetScale = extractScale(geom.offset); + + for (int frame = 0; frame < frameCount; frame++) { // init all joints in animation to bind pose - _anim[i].reserve(skeletonJointCount); - for (int j = 0; j < skeletonJointCount; j++) { - _anim[i].push_back(_skeleton->getRelativeBindPose(j)); + // this will give us a resonable result for bones in the skeleton but not in the animation. + _anim[frame].reserve(skeletonJointCount); + for (int skeletonJoint = 0; skeletonJoint < skeletonJointCount; skeletonJoint++) { + _anim[frame].push_back(_skeleton->getRelativeBindPose(skeletonJoint)); } - // init over all joint animations - for (int j = 0; j < animJointCount; j++) { - int k = jointMap[j]; - if (k >= 0 && k < skeletonJointCount) { - _anim[i][k].rot = _skeleton->getRelativeBindPose(k).rot * geom.animationFrames[i].rotations[j]; + for (int animJoint = 0; animJoint < animJointCount; animJoint++) { - // TODO -- why does applying all the joint translations make a mutant? - if (animJoints[j].parentIndex == -1) { - _anim[i][k].trans = geom.animationFrames[i].translations[j] * extractScale(geom.offset); - } else { - _anim[i][k].trans = _skeleton->getRelativeBindPose(k).trans; - } + int skeletonJoint = jointMap[animJoint]; + + // skip joints that are in the animation but not in the skeleton. + if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { + + const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint] * offsetScale; + const AnimPose& relBindPose = _skeleton->getRelativeBindPose(skeletonJoint); + + // used to adjust translation offsets, so large translation animatons on the reference skeleton + // will be adjusted when played on a skeleton with short limbs. + float limbLengthScale = fabs(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relBindPose.trans) / glm::length(fbxZeroTrans)); + + AnimPose& pose = _anim[frame][skeletonJoint]; + const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; + + // rotation in fbxAnimationFrame is a delta from a reference skeleton bind pose. + pose.rot = relBindPose.rot * fbxAnimFrame.rotations[animJoint]; + + // translation in fbxAnimationFrame is not a delta. + // convert it into a delta by subtracting from the first frame. + const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint] * offsetScale; + pose.trans = relBindPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans); } } } + _poses.resize(skeletonJointCount); } From a1b54945d6d707d243291c91133bd9b235b597fa Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 2 Oct 2015 10:34:34 -0700 Subject: [PATCH 09/10] Compute the model offset in the rig, using the bind pose of the hips. --- interface/src/avatar/SkeletonModel.cpp | 3 +++ libraries/animation/src/Rig.cpp | 9 +++++++++ libraries/animation/src/Rig.h | 2 ++ 3 files changed, 14 insertions(+) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index beb58e746c..16a6fa0531 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -246,6 +246,9 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Hand* hand = _owningAvatar->getHand(); hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); + // let rig compute the model offset + setOffset(_rig->getModelOffset()); + // Don't Relax toward hand positions when in animGraph mode. if (!_rig->getEnableAnimGraph()) { const float HAND_RESTORATION_RATE = 0.25f; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index e83a63e329..25c10d7a61 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1263,3 +1263,12 @@ void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; }); } + +glm::vec3 Rig::getModelOffset() const { + if (_animSkeleton && _rootJointIndex >= 0) { + return -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; + } else { + const glm::vec3 DEFAULT_MODEL_OFFSET(0.0f, 0.0f, 0.0f); + return DEFAULT_MODEL_OFFSET; + } +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c99db65081..c57b4f629f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -204,6 +204,8 @@ public: AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) + glm::vec3 getModelOffset() const; + protected: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); From c0be32d35939ea15c5fb01511cbed6c9448564d3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 2 Oct 2015 11:40:46 -0700 Subject: [PATCH 10/10] Fix for model offset when using Rig Animations. --- interface/src/avatar/SkeletonModel.cpp | 5 ++++- libraries/animation/src/Rig.cpp | 8 ++++---- libraries/animation/src/Rig.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c308f5f041..a892447961 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -247,7 +247,10 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); // let rig compute the model offset - setOffset(_rig->getModelOffset()); + glm::vec3 modelOffset; + if (_rig->getModelOffset(modelOffset)) { + setOffset(modelOffset); + } // Don't Relax toward hand positions when in animGraph mode. if (!_rig->getEnableAnimGraph()) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 74108eade2..ee3ac0a05a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1203,11 +1203,11 @@ void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { }); } -glm::vec3 Rig::getModelOffset() const { +bool Rig::getModelOffset(glm::vec3& modelOffsetOut) const { if (_animSkeleton && _rootJointIndex >= 0) { - return -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; + modelOffsetOut = -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; + return true; } else { - const glm::vec3 DEFAULT_MODEL_OFFSET(0.0f, 0.0f, 0.0f); - return DEFAULT_MODEL_OFFSET; + return false; } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 3ad2539da5..ef802e925d 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -204,7 +204,7 @@ public: AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) - glm::vec3 getModelOffset() const; + bool getModelOffset(glm::vec3& modelOffsetOut) const; protected: