diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index ebcb8cfa34..f2115810c4 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -60,7 +60,7 @@ bool FaceModel::render(float alpha) { void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(_rotation); - glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * + glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation))); state.rotation = glm::angleAxis(-_owningHead->getRoll(), glm::normalize(inverse * axes[2])) * glm::angleAxis(_owningHead->getYaw(), glm::normalize(inverse * axes[1])) * diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 6723b766e8..1cfdfbca88 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -154,7 +154,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - setJointPosition(jointIndex, palm.getPosition()); float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; glm::quat palmRotation; getJointRotation(jointIndex, palmRotation, true); @@ -186,6 +185,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin // no point in continuing if there are no fingers if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) { + stretchArm(jointIndex, palm.getPosition()); return; } @@ -203,6 +203,8 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true); } + + stretchArm(jointIndex, palm.getPosition()); } void SkeletonModel::updateJointState(int index) { @@ -219,9 +221,47 @@ void SkeletonModel::updateJointState(int index) { void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(_rotation); - glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * + glm::mat3 inverse = glm::mat3(glm::inverse(parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation))); state.rotation = glm::angleAxis(-_owningAvatar->getHead().getLeanSideways(), glm::normalize(inverse * axes[2])) * glm::angleAxis(-_owningAvatar->getHead().getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation; } +void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) { + // find out where the hand is pointing + glm::quat handRotation; + getJointRotation(jointIndex, handRotation, true); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + glm::vec3 forwardVector(jointIndex == geometry.rightHandJointIndex ? -1.0f : 1.0f, 0.0f, 0.0f); + glm::vec3 handVector = handRotation * forwardVector; + + // align elbow with hand + const FBXJoint& joint = geometry.joints.at(jointIndex); + if (joint.parentIndex == -1) { + return; + } + glm::quat elbowRotation; + getJointRotation(joint.parentIndex, elbowRotation, true); + applyRotationDelta(joint.parentIndex, rotationBetween(elbowRotation * forwardVector, handVector), false); + + // set position according to normal length + float scale = extractUniformScale(_scale); + glm::vec3 handPosition = position - _translation; + glm::vec3 elbowPosition = handPosition - handVector * joint.distanceToParent * scale; + + // set shoulder orientation to point to elbow + const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex); + if (parentJoint.parentIndex == -1) { + return; + } + glm::quat shoulderRotation; + getJointRotation(parentJoint.parentIndex, shoulderRotation, true); + applyRotationDelta(parentJoint.parentIndex, rotationBetween(shoulderRotation * forwardVector, + elbowPosition - extractTranslation(_jointStates.at(parentJoint.parentIndex).transform)), false); + + // update the shoulder state + updateJointState(parentJoint.parentIndex); + + // adjust the elbow's local translation + setJointTranslation(joint.parentIndex, elbowPosition); +} diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index caf08c823b..776eb29e3b 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -40,6 +40,10 @@ protected: private: + /// Using the current position and rotation of the identified (hand) joint, computes a + /// reasonable stretched configuration for the connected arm. + void stretchArm(int jointIndex, const glm::vec3& position); + Avatar* _owningAvatar; }; diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 1058bd02d9..5317eb8ff1 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -484,6 +484,7 @@ public: int parentIndex; + glm::vec3 translation; glm::mat4 preTransform; glm::quat preRotation; glm::quat rotation; @@ -499,8 +500,8 @@ glm::mat4 getGlobalTransform(const QMultiHash& parentMap, glm::mat4 globalTransform; while (!nodeID.isNull()) { const FBXModel& model = models.value(nodeID); - globalTransform = model.preTransform * glm::mat4_cast(model.preRotation * model.rotation * model.postRotation) * - model.postTransform * globalTransform; + globalTransform = glm::translate(model.translation) * model.preTransform * glm::mat4_cast(model.preRotation * + model.rotation * model.postRotation) * model.postTransform * globalTransform; QList parentIDs = parentMap.values(nodeID); nodeID = QString(); @@ -956,8 +957,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html - model.preTransform = glm::translate(translation) * glm::translate(rotationOffset) * - glm::translate(rotationPivot); + model.translation = translation; + model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); model.preRotation = glm::quat(glm::radians(preRotation)); model.rotation = glm::quat(glm::radians(rotation)); model.postRotation = glm::quat(glm::radians(postRotation)); @@ -1142,6 +1143,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } joint.freeLineage.remove(lastFreeIndex + 1, joint.freeLineage.size() - lastFreeIndex - 1); + joint.translation = model.translation; joint.preTransform = model.preTransform; joint.preRotation = model.preRotation; joint.rotation = model.rotation; @@ -1151,13 +1153,14 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) joint.rotationMax = model.rotationMax; glm::quat combinedRotation = model.preRotation * model.rotation * model.postRotation; if (joint.parentIndex == -1) { - joint.transform = geometry.offset * model.preTransform * glm::mat4_cast(combinedRotation) * model.postTransform; + joint.transform = geometry.offset * glm::translate(model.translation) * model.preTransform * + glm::mat4_cast(combinedRotation) * model.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation); joint.distanceToParent = 0.0f; } else { const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex); - joint.transform = parentJoint.transform * + joint.transform = parentJoint.transform * glm::translate(model.translation) * model.preTransform * glm::mat4_cast(combinedRotation) * model.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation) * parentJoint.inverseDefaultRotation; joint.distanceToParent = glm::distance(extractTranslation(parentJoint.transform), diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index 6cc08a1549..45410500d9 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -48,6 +48,7 @@ public: int parentIndex; float distanceToParent; float boneRadius; + glm::vec3 translation; glm::mat4 preTransform; glm::quat preRotation; glm::quat rotation; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index e7fb6c7c1d..3eb1256f0f 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -99,6 +99,7 @@ void Model::simulate(float deltaTime) { if (_jointStates.isEmpty()) { foreach (const FBXJoint& joint, geometry.joints) { JointState state; + state.translation = joint.translation; state.rotation = joint.rotation; _jointStates.append(state); } @@ -626,7 +627,7 @@ void Model::updateJointState(int index) { glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset); glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation; - state.transform = baseTransform * geometry.offset * joint.preTransform * + state.transform = baseTransform * geometry.offset * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; state.combinedRotation = _rotation * combinedRotation; @@ -642,7 +643,7 @@ void Model::updateJointState(int index) { maybeUpdateEyeRotation(parentState, joint, state); } glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation; - state.transform = parentState.transform * joint.preTransform * + state.transform = parentState.transform * glm::translate(state.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; state.combinedRotation = parentState.combinedRotation * combinedRotation; } @@ -747,6 +748,23 @@ bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fro return true; } +void Model::setJointTranslation(int jointIndex, const glm::vec3& translation) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const FBXJoint& joint = geometry.joints.at(jointIndex); + + glm::mat4 parentTransform; + if (joint.parentIndex == -1) { + parentTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + + } else { + parentTransform = _jointStates.at(joint.parentIndex).transform; + } + JointState& state = _jointStates[jointIndex]; + glm::vec3 preTranslation = extractTranslation(joint.preTransform * glm::mat4_cast(joint.preRotation * + state.rotation * joint.postRotation) * joint.postTransform); + state.translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation; +} + bool Model::restoreJointPosition(int jointIndex, float percent) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; @@ -824,25 +842,6 @@ void Model::renderCollisionProxies(float alpha) { glPopMatrix(); } -void Model::setJointTranslation(int jointIndex, int parentIndex, int childIndex, const glm::vec3& translation) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - JointState& state = _jointStates[jointIndex]; - if (childIndex != -1 && geometry.joints.at(jointIndex).isFree) { - // if there's a child, then I must adjust *my* rotation - glm::vec3 childTranslation = extractTranslation(_jointStates.at(childIndex).transform); - applyRotationDelta(jointIndex, rotationBetween(childTranslation - extractTranslation(state.transform), - childTranslation - translation)); - } - if (parentIndex != -1 && geometry.joints.at(parentIndex).isFree) { - // if there's a parent, then I must adjust *its* rotation - JointState& parent = _jointStates[parentIndex]; - glm::vec3 parentTranslation = extractTranslation(parent.transform); - applyRotationDelta(parentIndex, rotationBetween(extractTranslation(state.transform) - parentTranslation, - translation - parentTranslation)); - } - ::setTranslation(state.transform, translation); -} - void Model::deleteGeometry() { foreach (Model* attachment, _attachments) { delete attachment; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 3b1f66938b..fc3a0687b8 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -141,6 +141,7 @@ protected: class JointState { public: + glm::vec3 translation; glm::quat rotation; glm::mat4 transform; glm::quat combinedRotation; @@ -172,6 +173,8 @@ protected: bool allIntermediatesFree = false, const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f)); bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false); + void setJointTranslation(int jointIndex, const glm::vec3& translation); + /// Restores the indexed joint to its default position. /// \param percent the percentage of the default position to apply (i.e., 0.25f to slerp one fourth of the way to /// the original position @@ -188,8 +191,6 @@ protected: private: - void setJointTranslation(int jointIndex, int parentIndex, int childIndex, const glm::vec3& translation); - void deleteGeometry(); float _pupilDilation;