| |
| |
| |
| |
|
|
| #include "TrajectoryCorrector.h" |
| #include "InverseKinematics.h" |
|
|
| #include "Utility.h" |
|
|
| #include <map> |
| #include <array> |
| #include <cmath> |
| #include <cstdlib> |
| #include <iostream> |
| using Pose = std::vector<Math::Transform>; |
|
|
| static const float pos_weight = 0.001f; |
| static const float vel_weight = 1.0f; |
| static const float acc_weight = 10.0f; |
|
|
|
|
| namespace { |
|
|
| |
| |
| bool DebugPrintIntervalsEnabled() |
| { |
| const char* v = std::getenv("MOTIONCORRECTION_DEBUG_INTERVALS"); |
| if (v == nullptr || v[0] == '\0') |
| { |
| return false; |
| } |
| |
| return v[0] != '0'; |
| } |
|
|
|
|
| void FilterContactIntervals( |
| std::vector<std::pair<int, int>>& contactIntervals, |
| const std::vector<float>& mask, |
| bool one_bone_contact = false) |
| { |
| std::vector<uint32_t> keepIntervals; |
| for (size_t i = 0; i < contactIntervals.size(); ++i) |
| { |
| const auto& interval = contactIntervals[i]; |
|
|
| bool startConstrained = (interval.first != 0 && mask[interval.first - 1]); |
| bool endConstrained; |
|
|
| endConstrained = (interval.second != mask.size() && mask[interval.second]); |
|
|
| if (one_bone_contact) |
| { |
| if (startConstrained || endConstrained) |
| { |
| continue; |
| } |
| } |
| else |
| { |
| |
| |
| |
| if (startConstrained && endConstrained) |
| { |
| continue; |
| } |
| } |
|
|
| keepIntervals.push_back(i); |
| } |
|
|
| for (size_t i = 0; i < keepIntervals.size(); ++i) |
| { |
| contactIntervals[i] = contactIntervals[keepIntervals[i]]; |
| } |
| contactIntervals.resize(keepIntervals.size()); |
| } |
|
|
| std::vector<std::pair<int, int>> ComputeContactIntervals( |
| const std::vector<float>& contacts, |
| const std::vector<float>& mask, |
| float contactThreshold) |
| { |
| |
| std::vector<float> contactsNoMask = contacts; |
| for (size_t i = 0; i < mask.size(); ++i) |
| { |
| if (mask[i]) |
| { |
| contactsNoMask[i] = 0; |
| } |
| } |
|
|
| |
| std::vector<std::pair<int, int>> contactIntervals; |
| int start = -1; |
| for (int frame = 0; frame < mask.size(); ++frame) |
| { |
| bool isContact = contactsNoMask[frame] > contactThreshold; |
| if (isContact && start == -1) |
| { |
| start = frame; |
| } |
| else if (!isContact && start != -1) |
| { |
| contactIntervals.emplace_back(start, frame); |
| start = -1; |
| } |
| } |
|
|
| |
| if (start != -1) |
| { |
| contactIntervals.emplace_back(start, mask.size()); |
| } |
| return contactIntervals; |
| } |
|
|
| void FindContactPoints( |
| std::vector<Math::Vector> &points, |
| std::vector<int> &inContact, |
| const std::vector<int>& joint_parents_vec, |
| int32_t jointIndex, |
| const std::vector<Pose> &poses, |
| const std::vector<std::pair<int, int>>& contactIntervals, |
| const std::vector<float>& mask, |
| size_t frameCount, |
| float minHeight) |
| { |
| |
| |
| |
| |
| inContact.clear(); |
| inContact.resize(frameCount, 0); |
| points.clear(); |
| points.resize(frameCount); |
| for (size_t i = 0; i < contactIntervals.size(); ++i) |
| { |
| const auto& interval = contactIntervals[i]; |
| int frame = -1; |
| bool startConstrained = (interval.first != 0 && mask[interval.first - 1]); |
| bool endConstrained; |
|
|
| endConstrained = (interval.second != mask.size() && mask[interval.second]); |
|
|
| |
| if (DebugPrintIntervalsEnabled()) |
| { |
| std::cout << "Interval " << i << ": start=" << interval.first |
| << ", end=" << interval.second |
| << ", startConstrained=" << startConstrained |
| << ", endConstrained=" << endConstrained << std::endl; |
| } |
|
|
| if(startConstrained) |
| { |
| |
| |
| frame = interval.first - 1; |
| } |
| else if (endConstrained) |
| { |
| |
| |
| frame = interval.second; |
| } |
| else |
| { |
| |
| frame = (interval.first + interval.second) / 2; |
| } |
|
|
| |
| Math::Vector target = Animation::JointLocalToGlobal(joint_parents_vec, jointIndex, poses[frame]).GetTranslation(); |
| for(int i = interval.first; i < interval.second; ++i) |
| { |
| Math::Vector framePt = Animation::JointLocalToGlobal(joint_parents_vec, jointIndex, poses[i]).GetTranslation(); |
| inContact[i] = 1; |
| points[i] = target; |
| if (!startConstrained && !endConstrained) |
| { |
| points[i].SetY(std::max(framePt.GetY(), minHeight)); |
| |
| |
| } |
| } |
| } |
| } |
|
|
| float TargetReachFalloff( |
| const std::vector<int>& joint_parents_vec, |
| const Pose& defaultPose, |
| int32_t jointIndex, |
| Animation::IKType ikType, |
| const Math::Vector& target, |
| const Pose& pose, |
| const Math::Transform& rootTx = Math::Transform::Identity) |
| { |
| float maxReach = defaultPose[jointIndex].GetTranslation().GetLength3(); |
| if (ikType == Animation::IKType::kTwoBone) |
| { |
| jointIndex = joint_parents_vec[jointIndex]; |
| ASSERT(jointIndex > -1); |
| maxReach += defaultPose[jointIndex].GetTranslation().GetLength3(); |
| } |
| |
| jointIndex = joint_parents_vec[jointIndex]; |
| ASSERT(jointIndex > -1); |
| const auto worldTx = Animation::JointLocalToGlobal(joint_parents_vec, jointIndex, pose, rootTx); |
|
|
| |
| float targetDist = target.GetDistance3(worldTx.GetTranslation()); |
| float tmp = Math::Max(targetDist / maxReach - 0.99f, 0.f) / 0.01f; |
| tmp = tmp * tmp; |
| return std::exp(-2.f * tmp * tmp); |
| } |
|
|
| void CorrectHipsY( |
| std::vector<Pose>& poses, |
| const std::vector<Pose>& targetPoses, |
| const std::vector<float>& fullBodyMask, |
| const std::vector<Animation::ContactInfo>& contacts, |
| float contactThreshold |
| ) |
| { |
| |
| auto N = poses.size(); |
| Eigen::MatrixXd x(N, 1); |
| Eigen::MatrixXd observations(N, 1); |
| Eigen::MatrixXd xfixed(N, 1); |
|
|
| |
| |
| Eigen::VectorXd yCorrectMargins(N); |
| for(size_t frame = 0; frame < N; ++frame) |
| { |
| yCorrectMargins[frame] = fullBodyMask[frame] ? 0.0f : -1.0f; |
| x(frame, 0) = ((float*)&poses[frame][0].GetTranslation())[1]; |
| observations(frame, 0) = ((float*)&targetPoses[frame][0].GetTranslation())[1]; |
| } |
|
|
| TrajectoryCorrector ycorrector( |
| yCorrectMargins, |
| pos_weight * 10, |
| vel_weight, |
| acc_weight * 0.1f |
| ); |
| ycorrector.Interpolate( |
| xfixed, |
| observations, |
| x |
| ); |
|
|
| |
| for (uint32_t frame = 0; frame < N; ++frame) |
| { |
| ((float*)&poses[frame][0].GetTranslation())[1] = float(xfixed(frame, 0)); |
| } |
| } |
|
|
| void SmoothChannels( |
| Eigen::MatrixXd &x, |
| const std::vector<float>& mask |
| ) |
| { |
| for( uint32_t i=0; i < mask.size(); ++i) |
| { |
| uint32_t i_prev = i == 0 ? 0 : i-1; |
| uint32_t i_next = std::min(uint32_t(i+1), uint32_t(mask.size()-1)); |
| if(i > 0 && mask[i] > 0 && mask[i_prev] == 0) |
| { |
| |
| |
| for(long j=0; j < x.cols(); ++j) |
| { |
| x(i, j) = 0.5f * (x(i_prev, j) + x(i_next, j)); |
| } |
| } |
| if(mask[i] > 0 && mask[i_next] == 0) |
| { |
| |
| |
| for(long j=0; j < x.cols(); ++j) |
| { |
| x(i, j) = 0.5f * (x(i_prev, j) + x(i_next, j)); |
| } |
| } |
| } |
| } |
|
|
|
|
| void CorrectHipsXZ( |
| std::vector<Pose>& poses, |
| const std::vector<Pose>& targetPoses, |
| const std::vector<float>& fullBodyMask, |
| const std::vector<float>& rootMask, |
| const std::vector<Animation::ContactInfo>& endEffectorPins, |
| const Eigen::VectorXd& velocity_weights, |
| float root_margin |
| ) |
| { |
| auto N = poses.size(); |
| Eigen::VectorXd margins(N); |
| for( size_t i = 0; i < N; ++i ) |
| { |
| margins[i] = fullBodyMask[i] ? 0.0f : -1.0f; |
| } |
|
|
| std::vector<float> rootCombinedMask(N, 0.0f); |
| for(size_t i = 0; i < N; ++i) |
| { |
| rootCombinedMask[i] = (fullBodyMask[i] > 0) || (rootMask[i] > 0); |
| if(rootMask[i] > 0 && margins[i] != 0) |
| { |
| margins[i] = root_margin; |
| } |
| for (auto& c : endEffectorPins) |
| { |
| if (c.contactMask[i] && margins[i] != 0) |
| { |
| margins[i] = root_margin; |
| } |
| } |
| } |
| TrajectoryCorrector xzcorrector( |
| margins, |
| pos_weight, |
| vel_weight, |
| acc_weight, |
| velocity_weights |
| ); |
|
|
| |
| Eigen::MatrixXd x(N, 2); |
| Eigen::MatrixXd observations(N, 2); |
| Eigen::MatrixXd x_fixed(N, 2); |
|
|
| observations.setZero(); |
| for (uint32_t frame = 0; frame < N; ++frame) |
| { |
| x(frame, 0) = ((float*)&poses[frame][0].GetTranslation())[0]; |
| x(frame, 1) = ((float*)&poses[frame][0].GetTranslation())[2]; |
|
|
| observations(frame, 0) = ((float*)&targetPoses[frame][0].GetTranslation())[0]; |
| observations(frame, 1) = ((float*)&targetPoses[frame][0].GetTranslation())[2]; |
| } |
|
|
| SmoothChannels(x, rootCombinedMask); |
|
|
| xzcorrector.Interpolate( |
| x_fixed, |
| observations, |
| x |
| ); |
|
|
| |
| for (uint32_t frame = 0; frame < N; ++frame) |
| { |
| ((float*)&poses[frame][0].GetTranslation())[0] = float(x_fixed(frame, 0)); |
| ((float*)&poses[frame][0].GetTranslation())[2] = float(x_fixed(frame, 1)); |
| } |
| } |
|
|
| void CorrectRotationsForBone( |
| std::vector<Pose>& poses, |
| const std::vector<Pose>& targetPoses, |
| const std::vector<float>& mask, |
| const TrajectoryCorrector& corrector, |
| int boneIdx, |
| bool performChannelSmoothing) |
| { |
| auto N = poses.size(); |
| Eigen::MatrixXd x(N, 1); |
| Eigen::MatrixXd observations(N, 1); |
| observations.setZero(); |
| Eigen::MatrixXd x_fixed(N, 1); |
|
|
| |
| |
| |
| |
|
|
| |
| std::vector<float> forwardUp(6 * N); |
| std::vector<float> targetForwardUp(6 * N); |
| for (uint32_t frame = 0; frame < N; ++frame) |
| { |
| auto q = poses[frame][boneIdx].GetRotation(); |
| auto forward = q.ZAxis(); |
| auto up = q.YAxis(); |
| forwardUp[N * 0 + frame] = forward.GetX(); |
| forwardUp[N * 1 + frame] = forward.GetY(); |
| forwardUp[N * 2 + frame] = forward.GetZ(); |
| forwardUp[N * 3 + frame] = up.GetX(); |
| forwardUp[N * 4 + frame] = up.GetY(); |
| forwardUp[N * 5 + frame] = up.GetZ(); |
|
|
| q = targetPoses[frame][boneIdx].GetRotation(); |
| forward = q.ZAxis(); |
| up = q.YAxis(); |
| targetForwardUp[N * 0 + frame] = forward.GetX(); |
| targetForwardUp[N * 1 + frame] = forward.GetY(); |
| targetForwardUp[N * 2 + frame] = forward.GetZ(); |
| targetForwardUp[N * 3 + frame] = up.GetX(); |
| targetForwardUp[N * 4 + frame] = up.GetY(); |
| targetForwardUp[N * 5 + frame] = up.GetZ(); |
| } |
|
|
| |
| for (uint32_t dim = 0; dim < 6; ++dim) |
| { |
| for (uint32_t frame = 0; frame < N; ++frame) |
| { |
| x(frame, 0) = forwardUp[N * dim + frame]; |
| observations(frame, 0) = mask[frame] * targetForwardUp[N * dim + frame]; |
| } |
|
|
| if (performChannelSmoothing) |
| { |
| SmoothChannels(x, mask); |
| } |
|
|
| corrector.Interpolate( |
| x_fixed, |
| observations, |
| x |
| ); |
|
|
| |
| for (uint32_t frame = 0; frame < N; ++frame) |
| { |
| forwardUp[N * dim + frame] = float(x_fixed(frame, 0)); |
| } |
| } |
|
|
| for (uint32_t frame = 0; frame < N; ++frame) |
| { |
| Math::Vector forward = { forwardUp[N * 0 + frame] ,forwardUp[N * 1 + frame] ,forwardUp[N * 2 + frame] }; |
| Math::Vector up = { forwardUp[N * 3 + frame] ,forwardUp[N * 4 + frame] ,forwardUp[N * 5 + frame] }; |
|
|
| forward.Normalize3(); |
| up.Normalize3(); |
|
|
| poses[frame][boneIdx].SetRotation(Math::Quaternion::LookRotation(forward, up)); |
| } |
| } |
|
|
| void CorrectJointRotations( |
| std::vector<Pose>& poses, |
| const std::vector<Pose>& targetPoses, |
| const std::vector<float>& fullBodyMask, |
| const Eigen::VectorXd& velocity_weights |
| ) |
| { |
| auto N = poses.size(); |
|
|
| |
| Eigen::VectorXd margins(N); |
| for( size_t i = 0; i < N; ++i ) |
| { |
| margins[i] = fullBodyMask[i] ? 0.0f : -1.0f; |
| } |
| TrajectoryCorrector corrector( |
| margins, |
| pos_weight * 10, |
| vel_weight, |
| acc_weight, |
| velocity_weights |
| ); |
|
|
| for (uint32_t boneIdx = 0; boneIdx < poses[0].size(); ++boneIdx) |
| { |
| CorrectRotationsForBone( |
| poses, |
| targetPoses, |
| fullBodyMask, |
| corrector, |
| boneIdx, |
| true |
| ); |
| } |
| } |
|
|
| void DoEffectorIK( |
| std::vector<Pose>& poses, |
| const std::vector<Pose>& targetPoses, |
| const std::vector<float>& fullBodyMask, |
| const std::vector<Animation::ContactInfo>& endEffectorPins, |
| const std::vector<int>& joint_parents_vec, |
| const std::vector<Math::Transform>& defaultPose |
| ) |
| { |
| |
| auto N = poses.size(); |
| std::map<uint32_t, std::vector<float>> jointCorrectionMasks; |
| std::vector<Pose> ikFixedPoses = poses; |
| for (auto& c : endEffectorPins) |
| { |
| auto jointIdx = c.jointIndex; |
|
|
| if(jointCorrectionMasks[jointIdx].empty()) |
| { |
| |
| |
| jointCorrectionMasks[jointIdx] = fullBodyMask; |
| } |
|
|
| |
| auto parentIdx = joint_parents_vec[jointIdx]; |
| if(jointCorrectionMasks[parentIdx].empty()) |
| { |
| |
| |
| jointCorrectionMasks[parentIdx] = fullBodyMask; |
| } |
|
|
| |
| |
| auto parentParentIdx = joint_parents_vec[parentIdx]; |
| if(c.contactType == Animation::kTwoBone) |
| { |
| if(jointCorrectionMasks[parentParentIdx].empty()) |
| { |
| |
| |
| jointCorrectionMasks[parentParentIdx] = fullBodyMask; |
| } |
| } |
|
|
| for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame) |
| { |
| if (c.contactMask[fixFrame]) |
| { |
| const auto targetGlobalTransform = Animation::JointLocalToGlobal(joint_parents_vec, jointIdx, targetPoses[fixFrame]); |
|
|
| |
| jointCorrectionMasks[parentIdx][fixFrame] = 1; |
| switch(c.contactType) |
| { |
| case Animation::kOneBone: |
| { |
| IK::OneBoneIk( |
| ikFixedPoses[fixFrame], |
| Math::Transform::Identity, |
| jointIdx, |
| 1.0, |
| targetGlobalTransform.GetTranslation(), |
| joint_parents_vec |
| ); |
| break; |
| } |
| case Animation::kTwoBone: |
| { |
| |
| jointCorrectionMasks[parentParentIdx][fixFrame] = 1; |
| IK::TwoBoneIk( |
| ikFixedPoses[fixFrame], |
| Math::Transform::Identity, |
| jointIdx, |
| 1.0, |
| targetGlobalTransform.GetTranslation(), |
| joint_parents_vec, |
| c.hintOffset |
| ); |
| break; |
| } |
| } |
|
|
| |
| |
| jointCorrectionMasks[jointIdx][fixFrame] = 1; |
| auto parentGlobalTransform = Animation::JointLocalToGlobal(joint_parents_vec, parentIdx, ikFixedPoses[fixFrame]); |
| ikFixedPoses[fixFrame][jointIdx].SetRotation( |
| targetGlobalTransform.GetRotation() * parentGlobalTransform.GetRotation().GetConjugate() |
| ); |
|
|
| } |
| } |
| } |
|
|
| |
| |
| |
| Eigen::VectorXd margins(N); |
| for( auto &kv : jointCorrectionMasks) |
| { |
| for( size_t i = 0; i < N; ++i ) |
| { |
| margins[i] = kv.second[i] ? 0.0f : -1.0f; |
| } |
| TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight); |
|
|
| CorrectRotationsForBone( |
| poses, |
| ikFixedPoses, |
| kv.second, |
| corrector, |
| kv.first, |
| false |
| ); |
| } |
| } |
|
|
| void DoContactIK( |
| std::vector<Pose>& poses, |
| const std::vector<float>& fullBodyMask, |
| const std::vector<Animation::ContactInfo>& contacts, |
| const std::vector<Animation::ContactInfo>& endEffectorPins, |
| const std::vector<int>& joint_parents_vec, |
| const std::vector<Math::Transform>& defaultPose, |
| float contactThreshold, |
| bool has_double_ankle_joints |
| ) |
| { |
| auto N = poses.size(); |
| Eigen::VectorXd margins = Eigen::VectorXd::Zero(N); |
|
|
| |
| std::map<uint32_t, std::vector<float>> jointCorrectionMasks; |
| std::vector<Pose> ikFixedPoses = poses; |
|
|
| |
| const std::vector<Pose> originalPoses = poses; |
|
|
| |
| std::map<uint32_t, std::vector<bool>> twoBoneContactFrames; |
|
|
| auto addEndEffectorMask = [&](uint32_t jointIdx, uint32_t parentIdx, std::vector<float>& jointMask) |
| { |
| auto it = std::find_if( |
| endEffectorPins.begin(), endEffectorPins.end(), |
| [&](const auto &c) |
| { |
| if(jointIdx == c.jointIndex) |
| { |
| return true; |
| } |
| return false; |
| } |
| ); |
| if(it == endEffectorPins.end()) |
| { |
| |
| |
| it = std::find_if( |
| endEffectorPins.begin(), endEffectorPins.end(), |
| [&](const auto &c) |
| { |
| if(parentIdx == c.jointIndex) |
| { |
| return true; |
| } |
| return false; |
| } |
| ); |
| } |
| if(it != endEffectorPins.end()) |
| { |
| const auto &msk = it->contactMask; |
| for(size_t i=0; i < msk.size(); ++i) |
| { |
| if(msk[i]) |
| { |
| jointMask[i] = 1.0f; |
| } |
| } |
| } |
| }; |
|
|
| |
| for (auto& c : contacts) |
| { |
| if(c.contactType != Animation::kTwoBone) |
| { |
| continue; |
| } |
| const auto jointIdx = c.jointIndex; |
| auto parentIdx = joint_parents_vec[jointIdx]; |
| auto parentParentIdx = joint_parents_vec[parentIdx]; |
|
|
| auto jointMask = fullBodyMask; |
| addEndEffectorMask(jointIdx, parentIdx, jointMask); |
|
|
| |
| |
| |
| if(jointCorrectionMasks[parentIdx].empty()) |
| { |
| jointCorrectionMasks[parentIdx] = jointMask; |
| } |
| if(jointCorrectionMasks[parentParentIdx].empty()) |
| { |
| jointCorrectionMasks[parentParentIdx] = jointMask; |
| } |
| if(jointCorrectionMasks[jointIdx].empty()) |
| { |
| jointCorrectionMasks[jointIdx] = jointMask; |
| } |
|
|
| |
| auto contactIntervals = ComputeContactIntervals(c.contactMask, jointMask, contactThreshold); |
| FilterContactIntervals(contactIntervals, jointMask); |
|
|
| std::vector<Math::Vector> contactPoints; |
| std::vector<int> inContact; |
| FindContactPoints( |
| contactPoints, |
| inContact, |
| joint_parents_vec, |
| jointIdx, |
| poses, |
| contactIntervals, |
| jointMask, |
| c.contactMask.size(), |
| c.minHeight |
| ); |
|
|
| for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame) |
| { |
| if (inContact[fixFrame]) |
| { |
| auto target = contactPoints[fixFrame]; |
| jointCorrectionMasks[parentIdx][fixFrame] = 1.0f; |
| jointCorrectionMasks[parentParentIdx][fixFrame] = 1.0f; |
| jointCorrectionMasks[jointIdx][fixFrame] = 1.0f; |
|
|
| |
| if (has_double_ankle_joints) |
| { |
| if (twoBoneContactFrames[jointIdx].empty()) |
| twoBoneContactFrames[jointIdx].resize(fullBodyMask.size(), false); |
| twoBoneContactFrames[jointIdx][fixFrame] = true; |
| } |
|
|
| |
| auto jointGlobalRotation = Animation::JointLocalToGlobal( |
| joint_parents_vec, |
| jointIdx, |
| ikFixedPoses[fixFrame] |
| ).GetRotation(); |
|
|
| const float w = TargetReachFalloff( |
| joint_parents_vec, |
| defaultPose, |
| jointIdx, |
| c.contactType, |
| target, |
| ikFixedPoses[fixFrame] |
| ); |
| |
|
|
| |
| auto origParentRotation = ikFixedPoses[fixFrame][parentIdx].GetRotation(); |
| auto origParentParentRotation = ikFixedPoses[fixFrame][parentParentIdx].GetRotation(); |
| IK::TwoBoneIk( |
| ikFixedPoses[fixFrame], |
| Math::Transform::Identity, |
| jointIdx, |
| 1.0f, |
| target, |
| joint_parents_vec, |
| c.hintOffset |
| ); |
| ikFixedPoses[fixFrame][parentIdx].SetRotation(Math::Quaternion::SLerp(origParentRotation, ikFixedPoses[fixFrame][parentIdx].GetRotation(), w)); |
| ikFixedPoses[fixFrame][parentParentIdx].SetRotation(Math::Quaternion::SLerp(origParentParentRotation, ikFixedPoses[fixFrame][parentParentIdx].GetRotation(), w)); |
|
|
| |
| auto parentGloblalRotation = Animation::JointLocalToGlobal( |
| joint_parents_vec, |
| parentIdx, |
| ikFixedPoses[fixFrame] |
| ).GetRotation(); |
|
|
| jointCorrectionMasks[jointIdx][fixFrame] = 1.0f; |
| ikFixedPoses[fixFrame][jointIdx].SetRotation( |
| jointGlobalRotation * parentGloblalRotation.GetConjugate() |
| ); |
|
|
| auto result = Animation::JointLocalToGlobal( |
| joint_parents_vec, |
| jointIdx, |
| ikFixedPoses[fixFrame] |
| ).GetTranslation(); |
| } |
| } |
|
|
| } |
|
|
| for( auto &kv : jointCorrectionMasks) |
| { |
| for( size_t i = 0; i < N; ++i ) |
| { |
| margins[i] = kv.second[i] ? 0.0f : -1.0f; |
| } |
| TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight); |
| CorrectRotationsForBone( |
| poses, |
| ikFixedPoses, |
| kv.second, |
| corrector, |
| kv.first, |
| false |
| ); |
| } |
| jointCorrectionMasks.clear(); |
|
|
| |
| for(auto &c : contacts) |
| { |
| if(c.contactType != Animation::kOneBone) |
| { |
| continue; |
| } |
| const auto jointIdx = c.jointIndex; |
| auto parentIdx = joint_parents_vec[jointIdx]; |
|
|
| |
| |
| |
| |
| auto jointMask = fullBodyMask; |
| addEndEffectorMask(jointIdx, parentIdx, jointMask); |
|
|
| |
| if(jointCorrectionMasks[parentIdx].empty()) |
| { |
| jointCorrectionMasks[parentIdx] = jointMask; |
| } |
|
|
| |
| auto contactIntervals = ComputeContactIntervals(c.contactMask, jointMask, contactThreshold); |
| FilterContactIntervals(contactIntervals, jointMask, true); |
| for(const auto &interval : contactIntervals) |
| { |
| for (int fixFrame = interval.first; fixFrame < interval.second; ++fixFrame) |
| { |
| |
| |
|
|
| |
| |
| |
| Math::Vector parentPos = Animation::JointLocalToGlobal(joint_parents_vec, parentIdx, ikFixedPoses[fixFrame]).GetTranslation(); |
| Math::Vector target = Animation::JointLocalToGlobal(joint_parents_vec, jointIdx, ikFixedPoses[fixFrame]).GetTranslation(); |
| float jointLength = (target - parentPos).GetLength3(); |
| for(int32_t i = 0; i < 10; ++i) |
| { |
| target.SetY(c.minHeight); |
| auto dir = (target - parentPos).GetNormalized3(); |
| target = parentPos + dir * jointLength; |
| } |
|
|
| IK::OneBoneIk( |
| ikFixedPoses[fixFrame], |
| Math::Transform::Identity, |
| jointIdx, |
| 1.0f, |
| target, |
| joint_parents_vec |
| ); |
| jointCorrectionMasks[parentIdx][fixFrame] = 1.0f; |
| } |
| } |
|
|
| } |
|
|
| |
| |
| |
| for( auto &kv : jointCorrectionMasks) |
| { |
| for( size_t i = 0; i < N; ++i ) |
| { |
| margins[i] = kv.second[i] ? 0.0f : -1.0f; |
| } |
| TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight); |
| CorrectRotationsForBone( |
| poses, |
| ikFixedPoses, |
| kv.second, |
| corrector, |
| kv.first, |
| false |
| ); |
| } |
|
|
| if (has_double_ankle_joints) |
| { |
| |
| std::map<uint32_t, std::map<uint32_t, Math::Vector>> savedFirstAnkleTargets; |
| std::map<uint32_t, std::map<uint32_t, Math::Vector>> savedToeTargets; |
| std::map<uint32_t, uint32_t> contactToToeIdx; |
|
|
| |
| for (const auto& tc : contacts) |
| { |
| if (tc.contactType == Animation::kOneBone) |
| { |
| |
| int parentIdx = joint_parents_vec[tc.jointIndex]; |
| if (parentIdx >= 0) |
| { |
| contactToToeIdx[parentIdx] = tc.jointIndex; |
| } |
| } |
| } |
|
|
| |
| for (auto& c : contacts) |
| { |
| if (c.contactType != Animation::kTwoBone) |
| continue; |
|
|
| const auto firstAnkleIdx = c.jointIndex; |
| const auto secondAnkleIdx = joint_parents_vec[firstAnkleIdx]; |
| const auto kneeIdx = joint_parents_vec[secondAnkleIdx]; |
| const auto hipIdx = joint_parents_vec[kneeIdx]; |
|
|
| if (hipIdx < 0) continue; |
|
|
| |
| auto it = twoBoneContactFrames.find(firstAnkleIdx); |
| if (it == twoBoneContactFrames.end()) |
| continue; |
| const auto& contactFrames = it->second; |
|
|
| |
| auto jointMask = fullBodyMask; |
| addEndEffectorMask(firstAnkleIdx, secondAnkleIdx, jointMask); |
|
|
| if (jointCorrectionMasks[kneeIdx].empty()) |
| jointCorrectionMasks[kneeIdx] = jointMask; |
| if (jointCorrectionMasks[hipIdx].empty()) |
| jointCorrectionMasks[hipIdx] = jointMask; |
|
|
| for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame) |
| { |
| |
| if (!contactFrames[fixFrame]) |
| continue; |
|
|
| |
| savedFirstAnkleTargets[firstAnkleIdx][fixFrame] = Animation::JointLocalToGlobal( |
| joint_parents_vec, firstAnkleIdx, ikFixedPoses[fixFrame]).GetTranslation(); |
|
|
| if (contactToToeIdx.count(firstAnkleIdx)) |
| { |
| savedToeTargets[firstAnkleIdx][fixFrame] = Animation::JointLocalToGlobal( |
| joint_parents_vec, contactToToeIdx[firstAnkleIdx], ikFixedPoses[fixFrame]).GetTranslation(); |
| } |
|
|
| |
| auto originalFirstAnkleGlobal = Animation::JointLocalToGlobal( |
| joint_parents_vec, firstAnkleIdx, originalPoses[fixFrame]); |
| auto originalSecondAnkleGlobal = Animation::JointLocalToGlobal( |
| joint_parents_vec, secondAnkleIdx, originalPoses[fixFrame]); |
|
|
| |
| auto deltaFirstToSecond = originalFirstAnkleGlobal.GetDeltaToOther(originalSecondAnkleGlobal); |
|
|
| |
| auto correctedFirstAnkleGlobal = Animation::JointLocalToGlobal( |
| joint_parents_vec, firstAnkleIdx, ikFixedPoses[fixFrame]); |
|
|
| |
| auto target = (deltaFirstToSecond * correctedFirstAnkleGlobal).GetTranslation(); |
|
|
| |
| auto currPos = Animation::JointLocalToGlobal( |
| joint_parents_vec, secondAnkleIdx, ikFixedPoses[fixFrame]).GetTranslation(); |
|
|
| |
| IK::TwoBoneIk( |
| ikFixedPoses[fixFrame], |
| Math::Transform::Identity, |
| secondAnkleIdx, |
| 1.0f, |
| target, |
| joint_parents_vec, |
| c.hintOffset |
| ); |
|
|
| |
| |
| |
|
|
| jointCorrectionMasks[kneeIdx][fixFrame] = 1.0f; |
| jointCorrectionMasks[hipIdx][fixFrame] = 1.0f; |
| } |
| } |
|
|
| |
| for (auto& kv : jointCorrectionMasks) |
| { |
| for (size_t i = 0; i < N; ++i) |
| margins[i] = kv.second[i] ? 0.0f : -1.0f; |
|
|
| TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight); |
| CorrectRotationsForBone(poses, ikFixedPoses, kv.second, corrector, kv.first, false); |
| } |
|
|
| |
| jointCorrectionMasks.clear(); |
|
|
| for (auto& c : contacts) |
| { |
| if (c.contactType != Animation::kTwoBone) |
| continue; |
|
|
| const auto firstAnkleIdx = c.jointIndex; |
| const auto secondAnkleIdx = joint_parents_vec[firstAnkleIdx]; |
|
|
| auto it = twoBoneContactFrames.find(firstAnkleIdx); |
| if (it == twoBoneContactFrames.end()) |
| continue; |
|
|
| |
| auto jointMask = fullBodyMask; |
| addEndEffectorMask(firstAnkleIdx, secondAnkleIdx, jointMask); |
|
|
| if (jointCorrectionMasks[secondAnkleIdx].empty()) |
| jointCorrectionMasks[secondAnkleIdx] = jointMask; |
| if (jointCorrectionMasks[firstAnkleIdx].empty()) |
| jointCorrectionMasks[firstAnkleIdx] = jointMask; |
|
|
| for (uint32_t fixFrame = 0; fixFrame < fullBodyMask.size(); ++fixFrame) |
| { |
| if (!it->second[fixFrame]) |
| continue; |
|
|
| |
| IK::OneBoneIk( |
| ikFixedPoses[fixFrame], |
| Math::Transform::Identity, |
| firstAnkleIdx, |
| 1.0f, |
| savedFirstAnkleTargets[firstAnkleIdx][fixFrame], |
| joint_parents_vec |
| ); |
| jointCorrectionMasks[secondAnkleIdx][fixFrame] = 1.0f; |
|
|
| |
| |
| |
| |
|
|
| |
| if (contactToToeIdx.count(firstAnkleIdx) && savedToeTargets[firstAnkleIdx].count(fixFrame)) |
| { |
| IK::OneBoneIk( |
| ikFixedPoses[fixFrame], |
| Math::Transform::Identity, |
| contactToToeIdx[firstAnkleIdx], |
| 1.0f, |
| savedToeTargets[firstAnkleIdx][fixFrame], |
| joint_parents_vec |
| ); |
| jointCorrectionMasks[firstAnkleIdx][fixFrame] = 1.0f; |
| } |
|
|
| |
| |
| |
| |
| } |
| } |
|
|
| |
| for (auto& kv : jointCorrectionMasks) |
| { |
| for (size_t i = 0; i < N; ++i) |
| margins[i] = kv.second[i] ? 0.0f : -1.0f; |
|
|
| TrajectoryCorrector corrector(margins, pos_weight * 10, vel_weight, acc_weight); |
| CorrectRotationsForBone(poses, ikFixedPoses, kv.second, corrector, kv.first, false); |
| } |
| } |
| } |
|
|
| } |
|
|
|
|
| Math::Transform Animation::JointLocalToGlobal( |
| const std::vector<int>& joint_parents_vec, |
| int32_t index, |
| const Pose& localPose, |
| const Math::Transform& rootTx) |
| { |
| Math::Transform worldTx = Math::Transform::Identity; |
| while (index > -1) |
| { |
| worldTx = worldTx * localPose[index]; |
| index = joint_parents_vec[index]; |
| } |
|
|
| return worldTx * rootTx; |
| } |
|
|
| void Animation::CorrectMotion( |
| std::vector<Pose>& poses, |
| const std::vector<Pose>& targetPoses, |
| const std::vector<float>& fullBodyMask, |
| const std::vector<float>& rootMask, |
| const std::vector<ContactInfo>& contacts, |
| const std::vector<ContactInfo>& endEffectorPins, |
| const std::vector<int>& joint_parents_vec, |
| const std::vector<Math::Transform>& defaultPose, |
| float contactThreshold, |
| float root_margin, |
| bool has_double_ankle_joints |
| ) |
| { |
|
|
| |
| |
| const uint32_t N = poses.size(); |
| Eigen::VectorXd velocity_weights(N); |
| for (uint32_t frame = 1; frame < N; ++frame) |
| { |
| |
| float xdiff = poses[frame][0].GetTranslation()[0] - poses[frame - 1][0].GetTranslation()[0]; |
| float zdiff = poses[frame][0].GetTranslation()[2] - poses[frame - 1][0].GetTranslation()[2]; |
|
|
| |
| float v_mag = sqrtf(xdiff*xdiff + zdiff*zdiff) / 0.05f; |
|
|
| |
| |
| v_mag = std::max(v_mag, 1.0f/1000.0f); |
| velocity_weights(frame) = 1.0f / v_mag; |
| } |
| velocity_weights[0] = velocity_weights[1]; |
|
|
| |
| |
| |
| |
| |
| |
| CorrectHipsY( |
| poses, |
| targetPoses, |
| fullBodyMask, |
| contacts, |
| contactThreshold |
| ); |
|
|
| |
| |
| |
| |
| CorrectHipsXZ( |
| poses, |
| targetPoses, |
| fullBodyMask, |
| rootMask, |
| endEffectorPins, |
| velocity_weights, |
| root_margin |
| ); |
|
|
| |
| |
| CorrectJointRotations( |
| poses, |
| targetPoses, |
| fullBodyMask, |
| velocity_weights |
| ); |
|
|
| |
| DoEffectorIK( |
| poses, |
| targetPoses, |
| fullBodyMask, |
| endEffectorPins, |
| joint_parents_vec, |
| defaultPose |
| ); |
|
|
| |
| DoContactIK( |
| poses, |
| fullBodyMask, |
| contacts, |
| endEffectorPins, |
| joint_parents_vec, |
| defaultPose, |
| contactThreshold, |
| has_double_ankle_joints |
| ); |
| |
| } |
|
|