| |
| |
| |
| |
|
|
| #include "InverseKinematics.h" |
| #include "Math/Scalar.h" |
| #include <iostream> |
|
|
|
|
| using namespace IK; |
|
|
| namespace |
| { |
|
|
| float getAngleWithTwoSideVectors(const Math::Vector& vecLeft, const Math::Vector& vecRight) |
| { |
| auto lNorm = vecLeft.GetNormalized3(); |
| auto rNorm = vecRight.GetNormalized3(); |
|
|
| float cosine = lNorm.GetDot3(rNorm); |
| float sine = lNorm.Cross3(rNorm).GetLength3(); |
|
|
| return atan2f(sine, cosine); |
| } |
|
|
| float getAngleWithCosineRule (const float lSideLeft, const float lSideRight, const float lSideAcross) |
| { |
| float val = |
| (lSideRight * lSideRight + lSideLeft * lSideLeft - lSideAcross * lSideAcross) / |
| (2.0f * lSideLeft * lSideRight); |
| val = Math::Clamp(val, -1.0f, 1.0f); |
| return acosf(val); |
| } |
|
|
| } |
|
|
|
|
| void IK::TwoBoneIk( |
| Pose& pose, |
| const Math::Transform& rootTransform, |
| uint32_t cIdx, |
| float weight, |
| const Math::Vector& target, |
| const std::vector<int>& joint_parents_vec, |
| const Math::Vector& hintOffset |
| ) |
| { |
| weight = Math::Clamp(weight, 0.0f, 1.0f); |
| if (!(weight > 0.0f)) |
| return; |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| int32_t bIdx = joint_parents_vec[cIdx]; |
| if (bIdx < 0) |
| { |
| return; |
| } |
| int32_t aIdx = joint_parents_vec[bIdx]; |
| if (aIdx < 0) |
| { |
| return; |
| } |
|
|
| |
| Math::Transform aParentWorldTransform = Math::Transform::Identity; |
| int32_t idx = joint_parents_vec[aIdx]; |
| while (idx >= 0) |
| { |
| aParentWorldTransform = aParentWorldTransform * pose[idx]; |
| idx = joint_parents_vec[idx]; |
| } |
| aParentWorldTransform = aParentWorldTransform * rootTransform; |
|
|
| |
| Math::Transform aWorld = pose[aIdx] * aParentWorldTransform; |
| Math::Transform bWorld = pose[bIdx] * aWorld; |
| Math::Transform cWorld = pose[cIdx] * bWorld; |
|
|
| auto a = aWorld.GetTranslation(); |
| auto b = bWorld.GetTranslation(); |
| auto c = cWorld.GetTranslation(); |
| auto t = Math::Vector::Lerp(c, target, weight); |
|
|
| |
| float eps = 0.0001f; |
| float l_ab = (b - a).Length3().GetX(); |
| float l_bc = (c - b).Length3().GetX(); |
| float l_at = (a - t).Length3().GetX(); |
| l_at = Math::Clamp(l_at, eps, (l_ab + l_bc) * 0.999f); |
|
|
| |
| float theta_bac_current = getAngleWithTwoSideVectors(a - b, a - c); |
| float theta_abc_current = getAngleWithTwoSideVectors(b - a, b - c); |
| |
| if (l_ab < eps || l_bc < eps || l_at < eps) |
| { |
| return; |
| } |
| float theta_bac_desired = getAngleWithCosineRule(l_ab, l_at, l_bc); |
| float theta_abc_desired = getAngleWithCosineRule(l_ab, l_bc, l_at); |
|
|
| |
| Math::Vector rotationAxis = Math::Vector::Cross3(c - a, bWorld.TransformPoint(hintOffset) - a); |
| float l = rotationAxis.GetLength3(); |
| if (l == 0) |
| { |
| rotationAxis = Math::Vector(0,0,1); |
| } |
| else |
| { |
| rotationAxis /= l; |
| } |
|
|
| |
| Math::Vector rotationAxisLocalInBSpace = bWorld.GetRotation().RotateVectorInverse(rotationAxis); |
| Math::Transform rotateInB( |
| Math::Quaternion(rotationAxisLocalInBSpace, |
| (theta_abc_desired - theta_abc_current)), Math::Vector::Zero); |
|
|
| pose[bIdx] = rotateInB * pose[bIdx]; |
|
|
| Math::Vector rotationAxisLocalInASpace = aWorld.GetRotation().RotateVectorInverse(rotationAxis); |
| Math::Transform rotateInA( |
| Math::Quaternion(rotationAxisLocalInASpace, |
| (theta_bac_desired - theta_bac_current)), Math::Vector::Zero); |
|
|
| pose[aIdx] = rotateInA * pose[aIdx]; |
|
|
| |
| aWorld = pose[aIdx] * aParentWorldTransform; |
|
|
| |
| auto acLocal = aWorld.GetRotation().RotateVectorInverse( |
| c - a); |
| auto atLocal = aWorld.GetRotation().RotateVectorInverse( |
| target - a); |
| Math::Transform rotateStageTwo( |
| Math::Quaternion::FromRotationBetweenVectors(acLocal, atLocal), Math::Vector::Zero |
| ); |
|
|
| pose[aIdx] = rotateStageTwo * pose[aIdx]; |
|
|
| } |
|
|
| void IK::OneBoneIk( |
| Pose& pose, |
| const Math::Transform& rootTransform, |
| uint32_t bIdx, |
| float weight, |
| const Math::Vector& target, |
| const std::vector<int>& joint_parents_vec |
| ) |
| { |
| weight = Math::Clamp(weight, 0.0f, 1.0f); |
| if (!(weight > 0.0f)) |
| return; |
|
|
| int32_t aIdx = joint_parents_vec[bIdx]; |
| if (aIdx < 0) |
| { |
| return; |
| } |
|
|
| |
| Math::Transform aParentWorldTransform = Math::Transform::Identity; |
| int32_t idx = joint_parents_vec[aIdx]; |
| while (idx >= 0) |
| { |
| aParentWorldTransform = aParentWorldTransform * pose[idx]; |
| idx = joint_parents_vec[idx]; |
| } |
| aParentWorldTransform = aParentWorldTransform * rootTransform; |
|
|
| |
| Math::Transform aWorld = pose[aIdx] * aParentWorldTransform; |
| Math::Transform bWorld = pose[bIdx] * aWorld; |
|
|
| auto abLocal = aWorld.GetRotation().RotateVectorInverse( |
| bWorld.GetTranslation() - aWorld.GetTranslation()); |
| auto atLocal = aWorld.GetRotation().RotateVectorInverse( |
| target - aWorld.GetTranslation()); |
|
|
| auto deltaRLocal = Math::Quaternion::NLerp(Math::Quaternion::Identity, Math::Quaternion::FromRotationBetweenVectors(abLocal, atLocal), weight); |
| pose[aIdx].SetRotation(deltaRLocal * pose[aIdx].GetRotation()); |
| } |
|
|