// // Created by liangshuochen on 12/06/2017. // #include "Constraint.h" #include "Armature.h" #include "Bone.h" DRAGONBONES_NAMESPACE_BEGIN Matrix Constraint::_helpMatrix; Transform Constraint::_helpTransform; Point Constraint::_helpPoint; void Constraint::_onClear() { _constraintData = nullptr; _armature = nullptr; _target = nullptr; _root = nullptr; _bone = nullptr; } void IKConstraint::_onClear() { Constraint::_onClear(); _scaleEnabled = false; _bendPositive = false; _weight = 1.0f; } void IKConstraint::_computeA() { const auto& ikGlobal = _target->global; auto& global = _root->global; auto& globalTransformMatrix = _root->globalTransformMatrix; auto radian = std::atan2(ikGlobal.y - global.y, ikGlobal.x - global.x); if (global.scaleX < 0.0f) { radian += Transform::PI; } global.rotation += Transform::normalizeRadian(radian - global.rotation) * _weight; global.toMatrix(globalTransformMatrix); } void IKConstraint::_computeB() { const auto boneLength = _bone->_boneData->length; const auto parent = _root; const auto& ikGlobal = _target->global; auto& parentGlobal = parent->global; auto& global = _bone->global; auto& globalTransformMatrix = _bone->globalTransformMatrix; const auto x = globalTransformMatrix.a * boneLength; const auto y = globalTransformMatrix.b * boneLength; const auto lLL = x * x + y * y; const auto lL = sqrt(lLL); auto dX = global.x - parentGlobal.x; auto dY = global.y - parentGlobal.y; const auto lPP = dX * dX + dY * dY; const auto lP = sqrt(lPP); const auto rawRadian = global.rotation; const auto rawParentRadian = parentGlobal.rotation; const auto rawRadianA = std::atan2(dY, dX); dX = ikGlobal.x - parentGlobal.x; dY = ikGlobal.y - parentGlobal.y; const auto lTT = dX * dX + dY * dY; const auto lT = sqrt(lTT); auto radianA = 0.0f; if (lL + lP <= lT || lT + lL <= lP || lT + lP <= lL) { radianA = std::atan2(ikGlobal.y - parentGlobal.y, ikGlobal.x - parentGlobal.x); if (lL + lP <= lT) {} else if (lP < lL) { radianA += Transform::PI; } } else { const auto h = (lPP - lLL + lTT) / (2.0f * lTT); const auto r = sqrt(lPP - h * h * lTT) / lT; const auto hX = parentGlobal.x + (dX * h); const auto hY = parentGlobal.y + (dY * h); const auto rX = -dY * r; const auto rY = dX * r; auto isPPR = false; const auto parentParent = parent->getParent(); if (parentParent != nullptr) { auto parentParentMatrix = parentParent->globalTransformMatrix; isPPR = parentParentMatrix.a * parentParentMatrix.d - parentParentMatrix.b * parentParentMatrix.c < 0.0f; } if (isPPR != _bendPositive) { global.x = hX - rX; global.y = hY - rY; } else { global.x = hX + rX; global.y = hY + rY; } radianA = std::atan2(global.y - parentGlobal.y, global.x - parentGlobal.x); } const auto dR = Transform::normalizeRadian(radianA - rawRadianA); parentGlobal.rotation = rawParentRadian + dR * _weight; parentGlobal.toMatrix(parent->globalTransformMatrix); // const auto currentRadianA = rawRadianA + dR * _weight; global.x = parentGlobal.x + cos(currentRadianA) * lP; global.y = parentGlobal.y + sin(currentRadianA) * lP; // auto radianB = std::atan2(ikGlobal.y - global.y, ikGlobal.x - global.x); if (global.scaleX < 0.0f) { radianB += Transform::PI; } global.rotation = parentGlobal.rotation + rawRadian - rawParentRadian + Transform::normalizeRadian(radianB - dR - rawRadian) * _weight; global.toMatrix(globalTransformMatrix); } void IKConstraint::init(ConstraintData* constraintData, Armature* armature) { if (_constraintData != nullptr) { return; } _constraintData = constraintData; _armature = armature; _target = _armature->getBone(_constraintData->target->name); _root = _armature->getBone(_constraintData->root->name); _bone = _constraintData->bone != nullptr ? _armature->getBone(_constraintData->bone->name) : nullptr; { const auto ikConstraintData = static_cast(_constraintData); _bendPositive = ikConstraintData->bendPositive; _scaleEnabled = ikConstraintData->scaleEnabled; _weight = ikConstraintData->weight; } _root->_hasConstraint = true; } void IKConstraint::update() { _root->updateByConstraint(); if (_bone != nullptr) { _bone->updateByConstraint(); _computeB(); } else { _computeA(); } } void IKConstraint::invalidUpdate() { _root->invalidUpdate(); if (_bone != nullptr) { _bone->invalidUpdate(); } } DRAGONBONES_NAMESPACE_END