#include "Runtime/Character/CIkChain.hpp" #include "Runtime/Character/CAnimData.hpp" namespace metaforce { void CIkChain::Update(float dt) { if (x44_24_activated) x40_time = zeus::min(x40_time + dt, 1.f); else x40_time = zeus::max(0.f, x40_time - dt); } void CIkChain::Deactivate() { x44_24_activated = false; } void CIkChain::Activate(const CAnimData& animData, const CSegId& segId, const zeus::CTransform& xf) { x0_bone = segId; const auto& info = animData.GetPoseBuilder().CharLayoutInfo(); x1_p1 = info->GetRootNode()->GetBoneMap()[x0_bone].x0_parentId; if (x1_p1 != 2) { x2_p2 = info->GetRootNode()->GetBoneMap()[x1_p1].x0_parentId; x4_p2p1Dir = info->GetFromParentUnrotated(x1_p1); x1c_p2p1Length = x4_p2p1Dir.magnitude(); x4_p2p1Dir = x4_p2p1Dir / x1c_p2p1Length; x10_p1BoneDir = info->GetFromParentUnrotated(x0_bone); x20_p1BoneLength = x10_p1BoneDir.magnitude(); x10_p1BoneDir = x10_p1BoneDir / x20_p1BoneLength; x34_holdPos = xf.origin; x24_holdRot = zeus::CQuaternion(xf.basis); x44_24_activated = true; } } void CIkChain::PreRender(CAnimData& animData, const zeus::CTransform& xf, const zeus::CVector3f& scale) { if (x40_time > 0.f) { zeus::CTransform p2Xf = animData.GetLocatorTransform(x2_p2, nullptr); zeus::CVector3f localDelta = xf.transposeRotate(x34_holdPos - xf.origin); localDelta /= scale; localDelta = p2Xf.transposeRotate(localDelta - p2Xf.origin); zeus::CQuaternion p2Rot = animData.PoseBuilder().GetTreeMap()[x2_p2].x4_rotation; zeus::CQuaternion p1Rot = animData.PoseBuilder().GetTreeMap()[x1_p1].x4_rotation; zeus::CQuaternion boneRot = animData.PoseBuilder().GetTreeMap()[x0_bone].x4_rotation; zeus::CQuaternion newP2Rot = p2Rot; zeus::CQuaternion newP1Rot = p1Rot; Solve(newP2Rot, newP1Rot, localDelta); zeus::CQuaternion newBoneRot = (zeus::CQuaternion((xf * p2Xf).basis) * p2Rot.inverse() * newP2Rot * newP1Rot).inverse() * x24_holdRot; if (x40_time < 1.f) { newP2Rot = zeus::CQuaternion::slerpShort(p2Rot, newP2Rot, x40_time); newP1Rot = zeus::CQuaternion::slerpShort(p1Rot, newP1Rot, x40_time); newBoneRot = zeus::CQuaternion::slerpShort(boneRot, newBoneRot, x40_time); } animData.PoseBuilder().GetTreeMap()[x2_p2].x4_rotation = newP2Rot; animData.PoseBuilder().GetTreeMap()[x1_p1].x4_rotation = newP1Rot; animData.PoseBuilder().GetTreeMap()[x0_bone].x4_rotation = newBoneRot; animData.MarkPoseDirty(); } } void CIkChain::Solve(zeus::CQuaternion& q1, zeus::CQuaternion& q2, const zeus::CVector3f& vec) { const float mag = vec.magnitude(); const float magSq = mag * mag; const float twoMag = (2.0f * mag); float f29 = std::acos(zeus::clamp(-1.f, (((x20_p1BoneLength * magSq) + x20_p1BoneLength) - (x1c_p2p1Length * x1c_p2p1Length)) / (twoMag * x20_p1BoneLength), 1.f)); float f30 = std::acos(zeus::clamp(-1.f, ((x1c_p2p1Length * (magSq - (x20_p1BoneLength * x20_p1BoneLength))) + x1c_p2p1Length) / (twoMag * x1c_p2p1Length), 1.f)); zeus::CVector3f vecA = q2.transform(x10_p1BoneDir); zeus::CVector3f crossVecA = x4_p2p1Dir.cross(vecA); float crossAMag = crossVecA.magnitude(); crossVecA *= zeus::CVector3f(1.f / crossVecA.magnitude()); float angle = std::asin(zeus::min(crossAMag, 1.f)); if (x4_p2p1Dir.dot(vecA) < 0.f) angle = M_PIF - angle; q2 = zeus::CQuaternion::fromAxisAngle(crossVecA, (f30 + f29) - angle) * q2; zeus::CVector3f v1 = q1.transform((x1c_p2p1Length * x4_p2p1Dir) + (x20_p1BoneLength * q2.transform(x10_p1BoneDir))); zeus::CVector3f v2 = q1.transform(vec); zeus::CVector3f crossVecB = v1.normalized().cross((1.f / mag) * v2); angle = std::asin(zeus::min(crossVecB.magnitude(), 1.f)); if (v1.dot((1.f / mag) * v2) < 0.f) angle = M_PIF - angle; q1 = zeus::CQuaternion::fromAxisAngle(crossVecB * (1.f / crossVecB.magnitude()), angle) * q1; } } // namespace metaforce