void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints) { _joints = joints; // build a cache of bind poses _absoluteBindPoses.reserve(joints.size()); _relativeBindPoses.reserve(joints.size()); // build a chache of default poses _absoluteDefaultPoses.reserve(joints.size()); _relativeDefaultPoses.reserve(joints.size()); _relativePreRotationPoses.reserve(joints.size()); _relativePostRotationPoses.reserve(joints.size()); // iterate over FBXJoints and extract the bind pose information. for (int i = 0; i < (int)joints.size(); i++) { // build pre and post transforms glm::mat4 preRotationTransform = _joints[i].preTransform * glm::mat4_cast(_joints[i].preRotation); glm::mat4 postRotationTransform = glm::mat4_cast(_joints[i].postRotation) * _joints[i].postTransform; _relativePreRotationPoses.push_back(AnimPose(preRotationTransform)); _relativePostRotationPoses.push_back(AnimPose(postRotationTransform)); // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); _relativeDefaultPoses.push_back(relDefaultPose); int parentIndex = getParentIndex(i); if (parentIndex >= 0) { _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); } else { _absoluteDefaultPoses.push_back(relDefaultPose); } // build relative and absolute bind poses if (_joints[i].bindTransformFoundInCluster) { // Use the FBXJoint::bindTransform, which is absolute model coordinates // i.e. not relative to it's parent. AnimPose absoluteBindPose(_joints[i].bindTransform); _absoluteBindPoses.push_back(absoluteBindPose); if (parentIndex >= 0) { AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); _relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose); } else { _relativeBindPoses.push_back(absoluteBindPose); } } else { // use default transform instead _relativeBindPoses.push_back(relDefaultPose); if (parentIndex >= 0) { _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relDefaultPose); } else { _absoluteBindPoses.push_back(relDefaultPose); } } } }
// rotate bone's y-axis with target. AnimPose boneLookAt(const glm::vec3& target, const AnimPose& bone) { glm::vec3 u, v, w; generateBasisVectors(target - bone.trans(), bone.rot() * Vectors::UNIT_X, u, v, w); glm::mat4 lookAt(glm::vec4(v, 0.0f), glm::vec4(u, 0.0f), // AJT: TODO REVISIT THIS, this could be -w. glm::vec4(glm::normalize(glm::cross(v, u)), 0.0f), glm::vec4(bone.trans(), 1.0f)); return AnimPose(lookAt); }
AnimPose AnimPose::inverse() const { return AnimPose(glm::inverse(static_cast<glm::mat4>(*this))); }
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "AnimSkeleton.h" #include <glm/gtx/transform.hpp> #include <GLMHelpers.h> #include "AnimationLogging.h" const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), glm::quat(), glm::vec3(0.0f)); AnimPose::AnimPose(const glm::mat4& mat) { scale = extractScale(mat); rot = glm::normalize(glm::quat_cast(mat)); trans = extractTranslation(mat); } glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const { return trans + (rot * (scale * rhs)); } glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const { return *this * rhs; }
AnimPose AnimPose::operator*(const AnimPose& rhs) const { return AnimPose(static_cast<glm::mat4>(*this) * static_cast<glm::mat4>(rhs)); }
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets) { _joints = joints; _jointsSize = (int)joints.size(); // build a cache of bind poses // build a chache of default poses _absoluteDefaultPoses.reserve(_jointsSize); _relativeDefaultPoses.reserve(_jointsSize); _relativePreRotationPoses.reserve(_jointsSize); _relativePostRotationPoses.reserve(_jointsSize); // iterate over HFMJoints and extract the bind pose information. for (int i = 0; i < _jointsSize; i++) { // build pre and post transforms glm::mat4 preRotationTransform = _joints[i].preTransform * glm::mat4_cast(_joints[i].preRotation); glm::mat4 postRotationTransform = glm::mat4_cast(_joints[i].postRotation) * _joints[i].postTransform; _relativePreRotationPoses.push_back(AnimPose(preRotationTransform)); _relativePostRotationPoses.push_back(AnimPose(postRotationTransform)); // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); int parentIndex = getParentIndex(i); if (parentIndex >= 0) { _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); } else { _absoluteDefaultPoses.push_back(relDefaultPose); } } for (int k = 0; k < _jointsSize; k++) { if (jointOffsets.contains(k)) { AnimPose localOffset(jointOffsets[k], glm::vec3()); _absoluteDefaultPoses[k] = _absoluteDefaultPoses[k] * localOffset; } } // re-compute relative poses _relativeDefaultPoses = _absoluteDefaultPoses; convertAbsolutePosesToRelative(_relativeDefaultPoses); for (int i = 0; i < _jointsSize; i++) { _jointIndicesByName[_joints[i].name] = i; } // build mirror map. _nonMirroredIndices.clear(); _mirrorMap.reserve(_jointsSize); for (int i = 0; i < _jointsSize; i++) { if (_joints[i].name != "Hips" && _joints[i].name != "Spine" && _joints[i].name != "Spine1" && _joints[i].name != "Spine2" && _joints[i].name != "Neck" && _joints[i].name != "Head" && !((_joints[i].name.startsWith("Left") || _joints[i].name.startsWith("Right")) && _joints[i].name != "LeftEye" && _joints[i].name != "RightEye")) { // HACK: we don't want to mirror some joints so we remember their indices // so we can restore them after a future mirror operation _nonMirroredIndices.push_back(i); } int mirrorJointIndex = -1; if (_joints[i].name.startsWith("Left")) { QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); mirrorJointIndex = nameToJointIndex(mirrorJointName); } else if (_joints[i].name.startsWith("Right")) { QString mirrorJointName = QString(_joints[i].name).replace(0, 5, "Left"); mirrorJointIndex = nameToJointIndex(mirrorJointName); } if (mirrorJointIndex >= 0) { _mirrorMap.push_back(mirrorJointIndex); } else { _mirrorMap.push_back(i); } } }
void AnimDebugDraw::update() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); if (!scene) { return; } if (!render::Item::isValidID(_itemID)) { return; } render::Transaction transaction; transaction.updateItem<AnimDebugDrawData>(_itemID, [&](AnimDebugDrawData& data) { const size_t VERTICES_PER_BONE = (6 + (NUM_CIRCLE_SLICES * 2) * 3); const size_t VERTICES_PER_LINK = 8 * 2; const size_t VERTICES_PER_RAY = 2; const float BONE_RADIUS = 0.01f; // 1 cm const float POSE_RADIUS = 0.1f; // 10 cm // figure out how many verts we will need. int numVerts = 0; for (auto& iter : _absolutePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; for (auto i = 0; i < skeleton->getNumJoints(); i++) { auto parentIndex = skeleton->getParentIndex(i); if (parentIndex >= 0) { numVerts += VERTICES_PER_LINK; } } } // count marker verts from shared DebugDraw singleton auto markerMap = DebugDraw::getInstance().getMarkerMap(); numVerts += (int)markerMap.size() * VERTICES_PER_BONE; auto myAvatarMarkerMap = DebugDraw::getInstance().getMyAvatarMarkerMap(); numVerts += (int)myAvatarMarkerMap.size() * VERTICES_PER_BONE; auto rays = DebugDraw::getInstance().getRays(); DebugDraw::getInstance().clearRays(); numVerts += (int)rays.size() * VERTICES_PER_RAY; // allocate verts! std::vector<AnimDebugDrawData::Vertex> vertices; vertices.resize(numVerts); //Vertex* verts = (Vertex*)data._vertexBuffer->editData(); AnimDebugDrawData::Vertex* v = nullptr; if (numVerts) { v = &vertices[0]; } // draw absolute poses for (auto& iter : _absolutePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); AnimPoseVec& absPoses = std::get<1>(iter.second); AnimPose rootPose = std::get<2>(iter.second); glm::vec4 color = std::get<3>(iter.second); for (int i = 0; i < skeleton->getNumJoints(); i++) { const float radius = BONE_RADIUS / (absPoses[i].scale().x * rootPose.scale().x); // draw bone addBone(rootPose, absPoses[i], radius, color, v); // draw link to parent auto parentIndex = skeleton->getParentIndex(i); if (parentIndex >= 0) { assert(parentIndex < skeleton->getNumJoints()); addLink(rootPose, absPoses[i], absPoses[parentIndex], radius, color, v); } } } // draw markers from shared DebugDraw singleton for (auto& iter : markerMap) { glm::quat rot = std::get<0>(iter.second); glm::vec3 pos = std::get<1>(iter.second); glm::vec4 color = std::get<2>(iter.second); const float radius = POSE_RADIUS; addBone(AnimPose::identity, AnimPose(glm::vec3(1), rot, pos), radius, color, v); } AnimPose myAvatarPose(glm::vec3(1), DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos()); for (auto& iter : myAvatarMarkerMap) { glm::quat rot = std::get<0>(iter.second); glm::vec3 pos = std::get<1>(iter.second); glm::vec4 color = std::get<2>(iter.second); const float radius = POSE_RADIUS; addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, color, v); } // draw rays from shared DebugDraw singleton for (auto& iter : rays) { addLine(std::get<0>(iter), std::get<1>(iter), std::get<2>(iter), v); } data._vertexBuffer->resize(sizeof(AnimDebugDrawData::Vertex) * numVerts); data._vertexBuffer->setSubData<AnimDebugDrawData::Vertex>(0, vertices); assert((!numVerts && !v) || (numVerts == (v - &vertices[0]))); render::Item::Bound theBound; for (int i = 0; i < numVerts; i++) { theBound += vertices[i].pos; } data._bound = theBound; data._isVisible = (numVerts > 0); data._indexBuffer->resize(sizeof(uint16_t) * numVerts); for (int i = 0; i < numVerts; i++) { data._indexBuffer->setSubData<uint16_t>(i, (uint16_t)i);; } }); scene->enqueueTransaction(transaction); }
void AnimDebugDraw::update() { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); if (!scene) { return; } render::PendingChanges pendingChanges; pendingChanges.updateItem<AnimDebugDrawData>(_itemID, [&](AnimDebugDrawData& data) { const size_t VERTICES_PER_BONE = (6 + (NUM_CIRCLE_SLICES * 2) * 3); const size_t VERTICES_PER_LINK = 8 * 2; const float BONE_RADIUS = 0.01f; // 1 cm const float POSE_RADIUS = 0.1f; // 10 cm // figure out how many verts we will need. int numVerts = 0; for (auto& iter : _absolutePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; for (auto i = 0; i < skeleton->getNumJoints(); i++) { auto parentIndex = skeleton->getParentIndex(i); if (parentIndex >= 0) { numVerts += VERTICES_PER_LINK; } } } // count marker verts from shared DebugDraw singleton auto markerMap = DebugDraw::getInstance().getMarkerMap(); numVerts += (int)markerMap.size() * VERTICES_PER_BONE; auto myAvatarMarkerMap = DebugDraw::getInstance().getMyAvatarMarkerMap(); numVerts += (int)myAvatarMarkerMap.size() * VERTICES_PER_BONE; // allocate verts! data._vertexBuffer->resize(sizeof(Vertex) * numVerts); Vertex* verts = (Vertex*)data._vertexBuffer->editData(); Vertex* v = verts; // draw absolute poses for (auto& iter : _absolutePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); AnimPoseVec& absPoses = std::get<1>(iter.second); AnimPose rootPose = std::get<2>(iter.second); glm::vec4 color = std::get<3>(iter.second); for (int i = 0; i < skeleton->getNumJoints(); i++) { const float radius = BONE_RADIUS / (absPoses[i].scale.x * rootPose.scale.x); // draw bone addBone(rootPose, absPoses[i], radius, v); // draw link to parent auto parentIndex = skeleton->getParentIndex(i); if (parentIndex >= 0) { assert(parentIndex < skeleton->getNumJoints()); addLink(rootPose, absPoses[i], absPoses[parentIndex], radius, color, v); } } } // draw markers from shared DebugDraw singleton for (auto& iter : markerMap) { glm::quat rot = std::get<0>(iter.second); glm::vec3 pos = std::get<1>(iter.second); glm::vec4 color = std::get<2>(iter.second); // TODO: currently ignored. Q_UNUSED(color); const float radius = POSE_RADIUS; addBone(AnimPose::identity, AnimPose(glm::vec3(1), rot, pos), radius, v); } AnimPose myAvatarPose(glm::vec3(1), DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos()); for (auto& iter : myAvatarMarkerMap) { glm::quat rot = std::get<0>(iter.second); glm::vec3 pos = std::get<1>(iter.second); glm::vec4 color = std::get<2>(iter.second); // TODO: currently ignored. Q_UNUSED(color); const float radius = POSE_RADIUS; addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, v); } assert(numVerts == (v - verts)); data._indexBuffer->resize(sizeof(uint16_t) * numVerts); uint16_t* indices = (uint16_t*)data._indexBuffer->editData(); for (int i = 0; i < numVerts; i++) { indices[i] = i; } }); scene->enqueuePendingChanges(pendingChanges); }
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints) { _joints = joints; // build a cache of bind poses _absoluteBindPoses.reserve(joints.size()); _relativeBindPoses.reserve(joints.size()); // build a chache of default poses _absoluteDefaultPoses.reserve(joints.size()); _relativeDefaultPoses.reserve(joints.size()); _relativePreRotationPoses.reserve(joints.size()); _relativePostRotationPoses.reserve(joints.size()); // iterate over FBXJoints and extract the bind pose information. for (int i = 0; i < (int)joints.size(); i++) { // build pre and post transforms glm::mat4 preRotationTransform = _joints[i].preTransform * glm::mat4_cast(_joints[i].preRotation); glm::mat4 postRotationTransform = glm::mat4_cast(_joints[i].postRotation) * _joints[i].postTransform; _relativePreRotationPoses.push_back(AnimPose(preRotationTransform)); _relativePostRotationPoses.push_back(AnimPose(postRotationTransform)); // build relative and absolute default poses glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * preRotationTransform * glm::mat4_cast(_joints[i].rotation) * postRotationTransform; AnimPose relDefaultPose(relDefaultMat); _relativeDefaultPoses.push_back(relDefaultPose); int parentIndex = getParentIndex(i); if (parentIndex >= 0) { _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); } else { _absoluteDefaultPoses.push_back(relDefaultPose); } // build relative and absolute bind poses if (_joints[i].bindTransformFoundInCluster) { // Use the FBXJoint::bindTransform, which is absolute model coordinates // i.e. not relative to it's parent. AnimPose absoluteBindPose(_joints[i].bindTransform); _absoluteBindPoses.push_back(absoluteBindPose); if (parentIndex >= 0) { AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); _relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose); } else { _relativeBindPoses.push_back(absoluteBindPose); } } else { // use default transform instead _relativeBindPoses.push_back(relDefaultPose); if (parentIndex >= 0) { _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relDefaultPose); } else { _absoluteBindPoses.push_back(relDefaultPose); } } } // build mirror map. _nonMirroredIndices.clear(); _mirrorMap.reserve(_joints.size()); for (int i = 0; i < (int)joints.size(); i++) { if (_joints[i].name.endsWith("tEye")) { // HACK: we don't want to mirror some joints so we remember their indices // so we can restore them after a future mirror operation _nonMirroredIndices.push_back(i); } int mirrorJointIndex = -1; if (_joints[i].name.startsWith("Left")) { QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); mirrorJointIndex = nameToJointIndex(mirrorJointName); } else if (_joints[i].name.startsWith("Right")) { QString mirrorJointName = QString(_joints[i].name).replace(0, 5, "Left"); mirrorJointIndex = nameToJointIndex(mirrorJointName); } if (mirrorJointIndex >= 0) { _mirrorMap.push_back(mirrorJointIndex); } else { _mirrorMap.push_back(i); } } }
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); }