Skip to content

Commit

Permalink
Merge pull request #46 from Silverlan/fix/ik_fixes
Browse files Browse the repository at this point in the history
IK System Fixes
  • Loading branch information
Silverlan authored Sep 24, 2023
2 parents fa99e20 + 8c9e23a commit 6dd504b
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 47 deletions.
2 changes: 1 addition & 1 deletion build_scripts/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ def download_addon(name,addonName,url,commitId=None):

curDir = os.getcwd()
if with_pfm:
download_addon("PFM","filmmaker","https://github.com/Silverlan/pfm.git","2fd541dc7cc9ce980d26b5f4a5e3804210bf646e")
download_addon("PFM","filmmaker","https://github.com/Silverlan/pfm.git","d4888b9b6895c163f30f8728a78e1b1fa4b714e0")
download_addon("model editor","tool_model_editor","https://github.com/Silverlan/pragma_model_editor.git","362981334d7b2f023dbcb1a2d1972fdc843b15e7")

if with_vr:
Expand Down
2 changes: 1 addition & 1 deletion build_scripts/scripts/third_party_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
get_submodule("bvh","https://github.com/madmann91/bvh.git","2fd0db6","v1")
get_submodule("bzip2","https://github.com/sergiud/bzip2.git","c4a14bb87ee395fb2c69ef5dbb50762fe862517e","cmake")
get_submodule("clip2tri","https://github.com/raptor/clip2tri.git","f62a734d22733814b8a970ed8a68a4d94c24fa5f")
get_submodule("cppbepuik","https://github.com/Silverlan/cppbepuik.git","b13b397399a516d9fbbed90883fb5a0b5bc7d3ac")
get_submodule("cppbepuik","https://github.com/Silverlan/cppbepuik.git","0e76ab441519b24a6142e22d251afdc9bc1e56a2")
get_submodule("eigen","https://gitlab.com/libeigen/eigen.git","969c31ee")
get_submodule("exprtk","https://github.com/ArashPartow/exprtk.git","f46bffcd6966d38a09023fb37ba9335214c9b959")
get_submodule("freetype","https://github.com/aseprite/freetype2.git","e8ebfe9")
Expand Down
4 changes: 2 additions & 2 deletions core/shared/include/pragma/util/ik.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ namespace pragma::ik {
};
class DLLNETWORK EllipseSwingLimit : public IJoint {
public:
EllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, umath::Radian maxAngleX, umath::Radian maxAngleY);
EllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, const Vector3 &axisBRight, const Vector3 &axisBUp, umath::Radian maxAngleX, umath::Radian maxAngleY);
~EllipseSwingLimit();
Vector3 GetAxisA() const;
Vector3 GetAxisB() const;
Expand Down Expand Up @@ -316,7 +316,7 @@ namespace pragma::ik {
PointOnLineJoint &AddPointOnLineJoint(Bone &bone0, Bone &bone1, const Vector3 &lineAnchor, const Vector3 &lineDirection, const Vector3 &anchorB);
RevoluteJoint &AddRevoluteJoint(Bone &bone0, Bone &bone1, const Vector3 &freeAxis);
SwingLimit &AddSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, float maxAngle);
EllipseSwingLimit &AddEllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, float maxAngleX, float maxAngleY);
EllipseSwingLimit &AddEllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, const Vector3 &axisBRight, const Vector3 &axisBUp, float maxAngleX, float maxAngleY);
LinearAxisLimit &AddLinearAxisLimit(Bone &bone0, Bone &bone1, const Vector3 &lineAnchor, const Vector3 &lineDirection, const Vector3 &anchorB, float minimumDistance, float maximumDistance);
TwistJoint &AddTwistJoint(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB);
TwistLimit &AddTwistLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, float maxAngle);
Expand Down
93 changes: 56 additions & 37 deletions core/shared/src/entities/components/ik_solver_component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <panima/bone.hpp>

using namespace pragma;

#pragma optimize("", off)
ComponentEventId IkSolverComponent::EVENT_INITIALIZE_SOLVER = pragma::INVALID_COMPONENT_ID;
ComponentEventId IkSolverComponent::EVENT_ON_IK_UPDATED = pragma::INVALID_COMPONENT_ID;
IkSolverComponent::ConstraintInfo::ConstraintInfo(BoneId bone0, BoneId bone1) : boneId0 {bone0}, boneId1 {bone1} {}
Expand Down Expand Up @@ -413,7 +413,7 @@ void IkSolverComponent::AddFixedConstraint(const ConstraintInfo &constraintInfo)
auto &joint = m_ikSolver->AddAngularJoint(*bone0, *bone1);
init_joint(constraintInfo, joint);
}
void IkSolverComponent::AddHingeConstraint(const ConstraintInfo &constraintInfo, umath::Degree minAngle, umath::Degree maxAngle, const Quat &offsetRotation, SignedAxis twistAxis)
void IkSolverComponent::AddHingeConstraint(const ConstraintInfo &constraintInfo, umath::Degree minAngle, umath::Degree maxAngle, const Quat &offsetRotation, SignedAxis etwistAxis)
{
pragma::ik::Bone *bone0, *bone1;
umath::ScaledTransform refPose0, refPose1;
Expand All @@ -426,7 +426,7 @@ void IkSolverComponent::AddHingeConstraint(const ConstraintInfo &constraintInfo,

// If the twist axis is NOT the X axis, we'll have to rotate
// the main axis around a bit and adjust the limits accordingly.
switch(twistAxis) {
switch(etwistAxis) {
case pragma::SignedAxis::X:
case pragma::SignedAxis::NegX:
break;
Expand Down Expand Up @@ -467,7 +467,7 @@ void IkSolverComponent::AddHingeConstraint(const ConstraintInfo &constraintInfo,
init_joint(constraintInfo, swingLimit);
}

void IkSolverComponent::AddBallSocketConstraint(const ConstraintInfo &constraintInfo, const EulerAngles &minLimits, const EulerAngles &maxLimits, SignedAxis twistAxis)
void IkSolverComponent::AddBallSocketConstraint(const ConstraintInfo &constraintInfo, const EulerAngles &minLimits, const EulerAngles &maxLimits, SignedAxis etwistAxis)
{
pragma::ik::Bone *bone0, *bone1;
umath::ScaledTransform refPose0, refPose1;
Expand All @@ -487,41 +487,17 @@ void IkSolverComponent::AddBallSocketConstraint(const ConstraintInfo &constraint

// If the twist axis is NOT the Z axis, we'll have to rotate
// the main axis around a bit and adjust the limits accordingly.
auto twistRotOffset = Model::GetTwistAxisRotationOffset(twistAxis);
auto twistRotOffset = Model::GetTwistAxisRotationOffset(etwistAxis);
uquat::inverse(twistRotOffset);
{
switch(twistAxis) {
case pragma::SignedAxis::X:
case pragma::SignedAxis::NegX:
umath::swap(effectiveMinLimits.r, effectiveMinLimits.y);
umath::swap(effectiveMaxLimits.r, effectiveMaxLimits.y);
break;
case pragma::SignedAxis::Y:
case pragma::SignedAxis::NegY:
umath::swap(effectiveMinLimits.p, effectiveMinLimits.r);
umath::swap(effectiveMaxLimits.p, effectiveMaxLimits.r);

umath::swap(effectiveMinLimits.y, effectiveMaxLimits.y);
effectiveMinLimits.y *= -1.f;
effectiveMaxLimits.y *= -1.f;

//umath::swap(effectiveMinLimits.p, effectiveMinLimits.y);
break;
case pragma::SignedAxis::Z:
case pragma::SignedAxis::NegZ:
umath::swap(effectiveMinLimits.p, effectiveMinLimits.y);
umath::swap(effectiveMaxLimits.p, effectiveMaxLimits.y);
break;
}
}

auto refRot1 = rotBone1 * twistRotOffset; //uquat::create(ang);
auto refRot1 = rotBone1 * twistRotOffset;

auto useEllipseSwingLimit = true;
if(umath::abs((effectiveMaxLimits.p - effectiveMinLimits.p) - (effectiveMaxLimits.y - effectiveMinLimits.y)) <= 0.01f) {
// Swing limits are the same on both axes, so we can use a simple swing limit (which is less expensive)
useEllipseSwingLimit = false;
}

if(!useEllipseSwingLimit) {
// Convert ellipse swing limit to general swing limit
auto maxSpan = umath::max(effectiveMaxLimits.p - effectiveMinLimits.p, effectiveMaxLimits.y - effectiveMinLimits.y);
Expand All @@ -533,25 +509,68 @@ void IkSolverComponent::AddBallSocketConstraint(const ConstraintInfo &constraint
effectiveMinLimits.y = yMid - maxSpan * 0.5f;
}

// This is not quite correct and can cause issues with certain configurations.
auto rotBone1WithOffset = refRot1 * uquat::create(EulerAngles(-(effectiveMaxLimits.p + effectiveMinLimits.p), effectiveMaxLimits.y + effectiveMinLimits.y, 0.f)); //-(effectiveMaxLimits.r + effectiveMinLimits.r)));
auto effectiveLimitSpan = effectiveMinLimits + effectiveMaxLimits;
float effectiveTwistSpan;

// Eliminate rotation around twist axis (twist is handled separately using a twist limit, see further below)
switch(etwistAxis) {
case SignedAxis::X:
case SignedAxis::NegX:
effectiveTwistSpan = effectiveLimitSpan.p;
effectiveLimitSpan.p = 0.f;
break;
case SignedAxis::Y:
case SignedAxis::NegY:
effectiveTwistSpan = effectiveLimitSpan.y;
effectiveLimitSpan.y = 0.f;
break;
case SignedAxis::Z:
case SignedAxis::NegZ:
effectiveTwistSpan = effectiveLimitSpan.r;
effectiveLimitSpan.r = 0.f;
break;
}

auto twistAxis = uquat::forward(twistRotOffset);
// We need to rotate the axis for cases where minLimitAngle != -maxLimitAngle, since the axis will be off-center in those cases
uvec::rotate(&twistAxis, uquat::create(effectiveLimitSpan));
auto q1 = twistAxis;
twistAxis = rotBone1 * twistAxis;
auto &axisA = twistAxis;

// Depending on the twist axis, we have to switch around the angle limits
if(etwistAxis == SignedAxis::X || etwistAxis == SignedAxis::NegX) {
effectiveMinLimits = {effectiveMinLimits.r, effectiveMinLimits.y, effectiveMinLimits.p};
effectiveMaxLimits = {effectiveMaxLimits.r, effectiveMaxLimits.y, effectiveMaxLimits.p};
}
if(etwistAxis == SignedAxis::Y || etwistAxis == SignedAxis::NegY) {
effectiveMinLimits = {effectiveMinLimits.p, effectiveMinLimits.r, effectiveMinLimits.y};
effectiveMaxLimits = {effectiveMaxLimits.p, effectiveMaxLimits.r, effectiveMaxLimits.y};
}
if(etwistAxis == SignedAxis::Z || etwistAxis == SignedAxis::NegZ) {
// No need to do anything
}

auto axisB = uquat::forward(refRot1);
auto axisBRight = uquat::right(refRot1);
auto axisBUp = uquat::up(refRot1);
auto span = effectiveMaxLimits.y - effectiveMinLimits.y;
if(span >= 0.f && span < 179.99f) {
if(!useEllipseSwingLimit) {
auto &ellipseSwingLimit = m_ikSolver->AddSwingLimit(*bone0, *bone1, uquat::forward(rotBone1WithOffset), axisB, umath::deg_to_rad(effectiveMaxLimits.y - effectiveMinLimits.y));
auto &ellipseSwingLimit = m_ikSolver->AddSwingLimit(*bone0, *bone1, axisA, axisB, umath::deg_to_rad(effectiveMaxLimits.y - effectiveMinLimits.y));
init_joint(constraintInfo, ellipseSwingLimit);
}
else {
auto &ellipseSwingLimit = m_ikSolver->AddEllipseSwingLimit(*bone0, *bone1, uquat::forward(rotBone1WithOffset), axisB, umath::deg_to_rad(effectiveMaxLimits.y - effectiveMinLimits.y), umath::deg_to_rad(effectiveMaxLimits.p - effectiveMinLimits.p));
auto &ellipseSwingLimit = m_ikSolver->AddEllipseSwingLimit(*bone0, *bone1, axisA, axisB, axisBRight, axisBUp, umath::deg_to_rad(effectiveMaxLimits.y - effectiveMinLimits.y), umath::deg_to_rad(effectiveMaxLimits.p - effectiveMinLimits.p));
init_joint(constraintInfo, ellipseSwingLimit);
}
}

auto twistLimitVal = effectiveMaxLimits.r - effectiveMinLimits.r;
// Twist motion
auto twistLimitVal = effectiveTwistSpan;
if(twistLimitVal >= 0.f && twistLimitVal < 179.99f) {
auto &twistLimit = m_ikSolver->AddTwistLimit(*bone0, *bone1, uquat::forward(rotBone1WithOffset), axisB, umath::deg_to_rad(twistLimitVal));
// Note: Using a different max/min angle limit for twist rotation is currently not supported
auto &twistLimit = m_ikSolver->AddTwistLimit(*bone0, *bone1, axisA, axisB, umath::deg_to_rad(twistLimitVal));
init_joint(constraintInfo, twistLimit);
}
}
Expand Down
5 changes: 3 additions & 2 deletions core/shared/src/model/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1995,9 +1995,10 @@ Quat Model::GetTwistAxisRotationOffset(pragma::SignedAxis axis)
case pragma::SignedAxis::Y:
case pragma::SignedAxis::NegY:
return uquat::create(EulerAngles(pragma::is_negative_axis(axis) ? -90.f : 90.0, 0.f, 0.f));
case pragma::SignedAxis::Z:
case pragma::SignedAxis::NegZ:
return uquat::create(EulerAngles(0.0, 0.f, pragma::is_negative_axis(axis) ? -90.f : 90.f));
return uquat::create(EulerAngles(0.0, 0.f, 180.f));
case pragma::SignedAxis::Z:
break;
}
return uquat::identity();
}
8 changes: 4 additions & 4 deletions core/shared/src/util/ik.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,10 @@ void pragma::ik::SwingLimit::SetAxisA(const Vector3 &axisA) { static_cast<BEPUik
void pragma::ik::SwingLimit::SetAxisB(const Vector3 &axisB) { static_cast<BEPUik::IKSwingLimit *>(m_joint.get())->SetAxisB(to_bepu_axis(axisB)); }
void pragma::ik::SwingLimit::SetMaxAngle(umath::Radian maxAngle) { static_cast<BEPUik::IKSwingLimit *>(m_joint.get())->SetMaximumAngle(maxAngle); }

pragma::ik::EllipseSwingLimit::EllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, umath::Radian maxAngleX, umath::Radian maxAngleY)
pragma::ik::EllipseSwingLimit::EllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, const Vector3 &axisBRight, const Vector3 &axisBUp, umath::Radian maxAngleX, umath::Radian maxAngleY)
: IJoint {JointType::EllipseSwingLimit}, m_maxAngleX {maxAngleX}, m_maxAngleY {maxAngleY}, m_axisA {axisA}, m_axisB {axisB}
{
auto joint = std::make_unique<BEPUik::IKEllipseSwingLimit>(**bone0, **bone1, to_bepu_axis(axisA), to_bepu_axis(axisB), maxAngleX, maxAngleY);
auto joint = std::make_unique<BEPUik::IKEllipseSwingLimit>(**bone0, **bone1, to_bepu_axis(axisA), to_bepu_axis(axisB), to_bepu_axis(axisBRight), to_bepu_axis(axisBUp), maxAngleX, maxAngleY);
SetJoint(std::move(joint), bone0, bone1);
}
pragma::ik::EllipseSwingLimit::~EllipseSwingLimit() {}
Expand Down Expand Up @@ -456,9 +456,9 @@ pragma::ik::SwingLimit &pragma::ik::Solver::AddSwingLimit(Bone &bone0, Bone &bon
m_bepuJoints.push_back(**joint);
return *joint;
}
pragma::ik::EllipseSwingLimit &pragma::ik::Solver::AddEllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, float maxAngleX, float maxAngleY)
pragma::ik::EllipseSwingLimit &pragma::ik::Solver::AddEllipseSwingLimit(Bone &bone0, Bone &bone1, const Vector3 &axisA, const Vector3 &axisB, const Vector3 &axisBRight, const Vector3 &axisBUp, float maxAngleX, float maxAngleY)
{
auto joint = std::make_shared<EllipseSwingLimit>(bone0, bone1, axisA, axisB, maxAngleX, maxAngleY);
auto joint = std::make_shared<EllipseSwingLimit>(bone0, bone1, axisA, axisB, axisBRight, axisBUp, maxAngleX, maxAngleY);
m_joints.push_back(joint);
m_bepuJoints.push_back(**joint);
return *joint;
Expand Down

0 comments on commit 6dd504b

Please sign in to comment.