mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 06:44:06 +02:00
Relay joint translations across network. Apply animation's root-joint translation to avatar.
This commit is contained in:
parent
a820682816
commit
50dd8eba45
33 changed files with 585 additions and 84 deletions
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue