Relay joint translations across network. Apply animation's root-joint translation to avatar.

This commit is contained in:
Seth Alves 2015-09-12 08:23:50 -07:00
parent a820682816
commit 50dd8eba45
33 changed files with 585 additions and 84 deletions

View file

@ -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 {

View file

@ -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;

View file

@ -115,6 +115,7 @@ public:
virtual QVector<glm::quat> 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;

View file

@ -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<glm::quat> 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<glm::vec3> 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);
}

View file

@ -139,7 +139,10 @@ public:
void clearLookAtTargetAvatar();
virtual void setJointRotations(QVector<glm::quat> jointRotations);
virtual void setJointData(int index, const glm::quat& rotation);
virtual void setJointTranslations(QVector<glm::vec3> 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();

View file

@ -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);
}
}

View file

@ -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;
}
}
}
}

View file

@ -322,9 +322,11 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
std::map<int, RotationConstraint*>::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);

View file

@ -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

View file

@ -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));
}
}
}

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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() {

View file

@ -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

View file

@ -270,6 +270,7 @@ void Rig::reset(const QVector<FBXJoint>& 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;
}

View file

@ -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<FBXJoint>& 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;

View file

@ -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<bool> valids;
valids.resize(numJoints);
QVector<bool> 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<bool> 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<AvatarData*>(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<AvatarData*>(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<glm::quat> 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<glm::vec3> jointTranslations) {
if (QThread::currentThread() != thread()) {
QVector<glm::quat> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this),
"setJointTranslations", Qt::BlockingQueuedConnection,
Q_ARG(QVector<glm::vec3>, 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]);
}
}
}

View file

@ -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<JointData>& getJointData() const { return _jointData; }
void setJointData(const QVector<JointData>& 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<glm::quat> getJointRotations() const;
Q_INVOKABLE virtual void setJointRotations(QVector<glm::quat> jointRotations);
Q_INVOKABLE virtual void setJointTranslations(QVector<glm::vec3> 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 {

View file

@ -254,8 +254,17 @@ void Player::play() {
nextFrame.getJointRotations()[i],
_frameInterpolationFactor);
}
QVector<glm::vec3> 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<HeadData*>(_avatar->getHeadData());
if (head) {
// Make sure fake face tracker connection doesn't get turned off

View file

@ -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;

View file

@ -83,6 +83,7 @@ class RecordingFrame {
public:
QVector<float> getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
QVector<glm::quat> getJointRotations() const { return _jointRotations; }
QVector<glm::vec3> 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<float> blendshapeCoefficients);
void setJointRotations(QVector<glm::quat> jointRotations) { _jointRotations = jointRotations; }
void setJointTranslations(QVector<glm::vec3> 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<float> _blendshapeCoefficients;
QVector<glm::quat> _jointRotations;
QVector<glm::vec3> _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
#endif // hifi_Recording_h

View file

@ -276,10 +276,13 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
if (jointsMapped()) {
bool newFrame;
auto frameData = getAnimationFrame(newFrame);
QVector<glm::quat> frameDataRotations;
QVector<glm::vec3> 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);
}
}
}

View file

@ -218,18 +218,20 @@ void ModelEntityItem::mapJoints(const QStringList& modelJointNames) {
}
}
const QVector<glm::quat>& ModelEntityItem::getAnimationFrame(bool& newFrame) {
void ModelEntityItem::getAnimationFrame(bool& newFrame,
QVector<glm::quat>& rotationsResult, QVector<glm::vec3>& 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<FBXAnimationFrame>& 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<glm::quat>& ModelEntityItem::getAnimationFrame(bool& newFrame) {
if (animationFrameIndex != _lastKnownFrameIndex) {
_lastKnownFrameIndex = animationFrameIndex;
newFrame = true;
const QVector<glm::quat>& rotations = frames[animationFrameIndex].rotations;
_lastKnownFrameData.resize(_jointMapping.size());
const QVector<glm::quat>& rotations = frames[animationFrameIndex].rotations;
const QVector<glm::vec3>& 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 {

View file

@ -106,7 +106,7 @@ public:
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
void mapJoints(const QStringList& modelJointNames);
const QVector<glm::quat>& getAnimationFrame(bool& newFrame);
void getAnimationFrame(bool& newFrame, QVector<glm::quat>& rotationsResult, QVector<glm::vec3>& translationsResult);
bool jointsMapped() const { return _jointMappingCompleted; }
bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); }
@ -123,7 +123,8 @@ public:
static void cleanupLoadedAnimations();
protected:
QVector<glm::quat> _lastKnownFrameData;
QVector<glm::quat> _lastKnownFrameDataRotations;
QVector<glm::vec3> _lastKnownFrameDataTranslations;
int _lastKnownFrameIndex;

View file

@ -530,6 +530,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
QHash<QString, QString> typeFlags;
QHash<QString, QString> localRotations;
QHash<QString, QString> localTranslations;
QHash<QString, QString> xComponents;
QHash<QString, QString> yComponents;
QHash<QString, QString> 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()));
}
}

View file

@ -208,8 +208,8 @@ public:
/// A single animation frame extracted from an FBX document.
class FBXAnimationFrame {
public:
QVector<glm::quat> rotations;
QVector<glm::vec3> translations;
};
/// A light in an FBX document.

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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)

View file

@ -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;