void AnimClip::copyFromNetworkAnim() { assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); _anim.clear(); // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. const FBXGeometry& geom = _networkAnim->getGeometry(); AnimSkeleton animSkeleton(geom); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector<int> jointMap; jointMap.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { int skeletonJoint = _skeleton->nameToJointIndex(animSkeleton.getJointName(i)); if (skeletonJoint == -1) { qCWarning(animation) << "animation contains joint =" << animSkeleton.getJointName(i) << " which is not in the skeleton, url =" << _url; } jointMap.push_back(skeletonJoint); } const int frameCount = geom.animationFrames.size(); _anim.resize(frameCount); for (int frame = 0; frame < frameCount; frame++) { // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. _anim[frame].reserve(skeletonJointCount); for (int skeletonJoint = 0; skeletonJoint < skeletonJointCount; skeletonJoint++) { _anim[frame].push_back(_skeleton->getRelativeDefaultPose(skeletonJoint)); } for (int animJoint = 0; animJoint < animJointCount; animJoint++) { int skeletonJoint = jointMap[animJoint]; // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; #ifdef USE_PRE_ROT_FROM_ANIM // TODO: This is the correct way to apply the pre rotations from maya, however // the current animation set has incorrect preRotations for the left wrist and thumb // so it looks wrong if we enable this code. glm::quat preRotation = animSkeleton.getPreRotation(animJoint); #else // TODO: This is the legacy approach, this does not work when animations and models do not // have the same set of pre rotations. For example when mixing maya models with blender animations. glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot; #endif const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); // used to adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans)); AnimPose& pose = _anim[frame][skeletonJoint]; const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; // rotation in fbxAnimationFrame is a delta from its preRotation. pose.rot = preRotation * fbxAnimFrame.rotations[animJoint]; // translation in fbxAnimationFrame is not a delta. // convert it into a delta by subtracting from the first frame. const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint]; pose.trans = relDefaultPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans); } } } _poses.resize(skeletonJointCount); }
void AnimClip::copyFromNetworkAnim() { assert(_networkAnim && _networkAnim->isLoaded() && _skeleton); _anim.clear(); // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. const FBXGeometry& geom = _networkAnim->getGeometry(); AnimSkeleton animSkeleton(geom); const auto animJointCount = animSkeleton.getNumJoints(); const auto skeletonJointCount = _skeleton->getNumJoints(); std::vector<int> jointMap; jointMap.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { int skeletonJoint = _skeleton->nameToJointIndex(animSkeleton.getJointName(i)); if (skeletonJoint == -1) { qCWarning(animation) << "animation contains joint =" << animSkeleton.getJointName(i) << " which is not in the skeleton, url =" << _url; } jointMap.push_back(skeletonJoint); } const int frameCount = geom.animationFrames.size(); _anim.resize(frameCount); for (int frame = 0; frame < frameCount; frame++) { const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; // init all joints in animation to default pose // this will give us a resonable result for bones in the model skeleton but not in the animation. _anim[frame].reserve(skeletonJointCount); for (int skeletonJoint = 0; skeletonJoint < skeletonJointCount; skeletonJoint++) { _anim[frame].push_back(_skeleton->getRelativeDefaultPose(skeletonJoint)); } for (int animJoint = 0; animJoint < animJointCount; animJoint++) { int skeletonJoint = jointMap[animJoint]; const glm::vec3& fbxAnimTrans = fbxAnimFrame.translations[animJoint]; const glm::quat& fbxAnimRot = fbxAnimFrame.rotations[animJoint]; // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { AnimPose preRot, postRot; if (usePreAndPostPoseFromAnim) { preRot = animSkeleton.getPreRotationPose(animJoint); postRot = animSkeleton.getPostRotationPose(animJoint); } else { // In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations. preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot, glm::vec3()); postRot = AnimPose::identity; } // cancel out scale preRot.scale = glm::vec3(1.0f); postRot.scale = glm::vec3(1.0f); AnimPose rot(glm::vec3(1.0f), fbxAnimRot, glm::vec3()); // adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); float boneLengthScale = 1.0f; const float EPSILON = 0.0001f; if (fabsf(glm::length(fbxZeroTrans)) > EPSILON) { boneLengthScale = glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans); } AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans + boneLengthScale * (fbxAnimTrans - fbxZeroTrans)); _anim[frame][skeletonJoint] = trans * preRot * rot * postRot; } } } // mirrorAnim will be re-built on demand, if needed. _mirrorAnim.clear(); _poses.resize(skeletonJointCount); }