Fixes the SkeletonIK twisting issue by using the skeleton global pose without overrides
This commit is contained in:
parent
63391f645c
commit
c1bc87ed0d
4 changed files with 78 additions and 94 deletions
|
@ -80,6 +80,15 @@
|
||||||
Returns the overall transform of the specified bone, with respect to the skeleton. Being relative to the skeleton frame, this is not the actual "global" transform of the bone.
|
Returns the overall transform of the specified bone, with respect to the skeleton. Being relative to the skeleton frame, this is not the actual "global" transform of the bone.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="get_bone_global_pose_no_override" qualifiers="const">
|
||||||
|
<return type="Transform">
|
||||||
|
</return>
|
||||||
|
<argument index="0" name="bone_idx" type="int">
|
||||||
|
</argument>
|
||||||
|
<description>
|
||||||
|
Returns the overall transform of the specified bone, with respect to the skeleton, but without any global pose overrides. Being relative to the skeleton frame, this is not the actual "global" transform of the bone.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="get_bone_name" qualifiers="const">
|
<method name="get_bone_name" qualifiers="const">
|
||||||
<return type="String">
|
<return type="String">
|
||||||
</return>
|
</return>
|
||||||
|
|
|
@ -236,9 +236,6 @@ void Skeleton::_notification(int p_what) {
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
Bone &b = bonesptr[order[i]];
|
Bone &b = bonesptr[order[i]];
|
||||||
|
|
||||||
if (b.global_pose_override_amount >= 0.999) {
|
|
||||||
b.pose_global = b.global_pose_override;
|
|
||||||
} else {
|
|
||||||
if (b.disable_rest) {
|
if (b.disable_rest) {
|
||||||
if (b.enabled) {
|
if (b.enabled) {
|
||||||
Transform pose = b.pose;
|
Transform pose = b.pose;
|
||||||
|
@ -247,17 +244,20 @@ void Skeleton::_notification(int p_what) {
|
||||||
}
|
}
|
||||||
if (b.parent >= 0) {
|
if (b.parent >= 0) {
|
||||||
b.pose_global = bonesptr[b.parent].pose_global * pose;
|
b.pose_global = bonesptr[b.parent].pose_global * pose;
|
||||||
|
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose;
|
||||||
} else {
|
} else {
|
||||||
b.pose_global = pose;
|
b.pose_global = pose;
|
||||||
|
b.pose_global_no_override = pose;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (b.parent >= 0) {
|
if (b.parent >= 0) {
|
||||||
b.pose_global = bonesptr[b.parent].pose_global;
|
b.pose_global = bonesptr[b.parent].pose_global;
|
||||||
|
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override;
|
||||||
} else {
|
} else {
|
||||||
b.pose_global = Transform();
|
b.pose_global = Transform();
|
||||||
|
b.pose_global_no_override = Transform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (b.enabled) {
|
if (b.enabled) {
|
||||||
Transform pose = b.pose;
|
Transform pose = b.pose;
|
||||||
|
@ -266,14 +266,18 @@ void Skeleton::_notification(int p_what) {
|
||||||
}
|
}
|
||||||
if (b.parent >= 0) {
|
if (b.parent >= 0) {
|
||||||
b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
|
b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
|
||||||
|
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * (b.rest * pose);
|
||||||
} else {
|
} else {
|
||||||
b.pose_global = b.rest * pose;
|
b.pose_global = b.rest * pose;
|
||||||
|
b.pose_global_no_override = b.rest * pose;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (b.parent >= 0) {
|
if (b.parent >= 0) {
|
||||||
b.pose_global = bonesptr[b.parent].pose_global * b.rest;
|
b.pose_global = bonesptr[b.parent].pose_global * b.rest;
|
||||||
|
b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest;
|
||||||
} else {
|
} else {
|
||||||
b.pose_global = b.rest;
|
b.pose_global = b.rest;
|
||||||
|
b.pose_global_no_override = b.rest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,7 +285,6 @@ void Skeleton::_notification(int p_what) {
|
||||||
if (b.global_pose_override_amount >= CMP_EPSILON) {
|
if (b.global_pose_override_amount >= CMP_EPSILON) {
|
||||||
b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
|
b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (b.global_pose_override_reset) {
|
if (b.global_pose_override_reset) {
|
||||||
b.global_pose_override_amount = 0.0;
|
b.global_pose_override_amount = 0.0;
|
||||||
|
@ -382,6 +385,13 @@ Transform Skeleton::get_bone_global_pose(int p_bone) const {
|
||||||
return bones[p_bone].pose_global;
|
return bones[p_bone].pose_global;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transform Skeleton::get_bone_global_pose_no_override(int p_bone) const {
|
||||||
|
ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform());
|
||||||
|
if (dirty)
|
||||||
|
const_cast<Skeleton *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
|
||||||
|
return bones[p_bone].pose_global_no_override;
|
||||||
|
}
|
||||||
|
|
||||||
// skeleton creation api
|
// skeleton creation api
|
||||||
void Skeleton::add_bone(const String &p_name) {
|
void Skeleton::add_bone(const String &p_name) {
|
||||||
ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1);
|
ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1);
|
||||||
|
@ -848,6 +858,7 @@ void Skeleton::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton::clear_bones_global_pose_override);
|
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton::clear_bones_global_pose_override);
|
||||||
ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton::set_bone_global_pose_override, DEFVAL(false));
|
ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton::set_bone_global_pose_override, DEFVAL(false));
|
||||||
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton::get_bone_global_pose);
|
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton::get_bone_global_pose);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton::get_bone_global_pose_no_override);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton::get_bone_custom_pose);
|
ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton::get_bone_custom_pose);
|
||||||
ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton::set_bone_custom_pose);
|
ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton::set_bone_custom_pose);
|
||||||
|
|
|
@ -88,6 +88,7 @@ private:
|
||||||
|
|
||||||
Transform pose;
|
Transform pose;
|
||||||
Transform pose_global;
|
Transform pose_global;
|
||||||
|
Transform pose_global_no_override;
|
||||||
|
|
||||||
bool custom_pose_enable;
|
bool custom_pose_enable;
|
||||||
Transform custom_pose;
|
Transform custom_pose;
|
||||||
|
@ -174,6 +175,7 @@ public:
|
||||||
void set_bone_rest(int p_bone, const Transform &p_rest);
|
void set_bone_rest(int p_bone, const Transform &p_rest);
|
||||||
Transform get_bone_rest(int p_bone) const;
|
Transform get_bone_rest(int p_bone) const;
|
||||||
Transform get_bone_global_pose(int p_bone) const;
|
Transform get_bone_global_pose(int p_bone) const;
|
||||||
|
Transform get_bone_global_pose_no_override(int p_bone) const;
|
||||||
|
|
||||||
void clear_bones_global_pose_override();
|
void clear_bones_global_pose_override();
|
||||||
void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false);
|
void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false);
|
||||||
|
|
|
@ -246,7 +246,7 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_
|
||||||
p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform;
|
p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform;
|
||||||
} else {
|
} else {
|
||||||
// End effector in local transform
|
// End effector in local transform
|
||||||
const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors[0].tip_bone));
|
const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose_no_override(p_task->end_effectors[0].tip_bone));
|
||||||
|
|
||||||
// Update the end_effector (local transform) by blending with current pose
|
// Update the end_effector (local transform) by blending with current pose
|
||||||
p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta);
|
p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta);
|
||||||
|
@ -270,18 +270,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
|
||||||
return; // Skip solving
|
return; // Skip solving
|
||||||
}
|
}
|
||||||
|
|
||||||
p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, true);
|
// Update the initial root transform so its synced with any animation changes
|
||||||
|
|
||||||
if (p_task->chain.middle_chain_item) {
|
|
||||||
p_task->skeleton->set_bone_global_pose_override(p_task->chain.middle_chain_item->bone, Transform(), 0.0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < p_task->chain.tips.size(); i += 1) {
|
|
||||||
p_task->skeleton->set_bone_global_pose_override(p_task->chain.tips[i].chain_item->bone, Transform(), 0.0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the initial root transform
|
|
||||||
// (Needed to sync IK with animation)
|
|
||||||
_update_chain(p_task->skeleton, &p_task->chain.chain_root);
|
_update_chain(p_task->skeleton, &p_task->chain.chain_root);
|
||||||
|
|
||||||
make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta);
|
make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta);
|
||||||
|
@ -298,31 +287,6 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
|
||||||
Transform new_bone_pose(ci->initial_transform);
|
Transform new_bone_pose(ci->initial_transform);
|
||||||
new_bone_pose.origin = ci->current_pos;
|
new_bone_pose.origin = ci->current_pos;
|
||||||
|
|
||||||
// The root bone needs to be rotated differently so it isn't frozen in place
|
|
||||||
if (ci == &p_task->chain.chain_root && !ci->children.empty()) {
|
|
||||||
new_bone_pose = new_bone_pose.looking_at(ci->children[0].current_pos, Vector3(0, 1, 0));
|
|
||||||
const Vector3 bone_rest_dir = p_task->skeleton->get_bone_rest(ci->children[0].bone).origin.normalized().abs();
|
|
||||||
const Vector3 bone_rest_dir_abs = bone_rest_dir.abs();
|
|
||||||
if (bone_rest_dir_abs.x > bone_rest_dir_abs.y && bone_rest_dir_abs.x > bone_rest_dir_abs.z) {
|
|
||||||
if (bone_rest_dir.x < 0) {
|
|
||||||
new_bone_pose.basis.rotate_local(Vector3(0, 1, 0), -Math_PI / 2.0f);
|
|
||||||
} else {
|
|
||||||
new_bone_pose.basis.rotate_local(Vector3(0, 1, 0), Math_PI / 2.0f);
|
|
||||||
}
|
|
||||||
} else if (bone_rest_dir_abs.y > bone_rest_dir_abs.x && bone_rest_dir_abs.y > bone_rest_dir_abs.z) {
|
|
||||||
if (bone_rest_dir.y < 0) {
|
|
||||||
new_bone_pose.basis.rotate_local(Vector3(1, 0, 0), Math_PI / 2.0f);
|
|
||||||
} else {
|
|
||||||
new_bone_pose.basis.rotate_local(Vector3(1, 0, 0), -Math_PI / 2.0f);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (bone_rest_dir.z < 0) {
|
|
||||||
// Do nothing!
|
|
||||||
} else {
|
|
||||||
new_bone_pose.basis.rotate_local(Vector3(0, 0, 1), Math_PI);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!ci->children.empty()) {
|
if (!ci->children.empty()) {
|
||||||
/// Rotate basis
|
/// Rotate basis
|
||||||
const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized());
|
const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized());
|
||||||
|
@ -335,13 +299,11 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Set target orientation to tip
|
// Set target orientation to tip
|
||||||
if (override_tip_basis) {
|
if (override_tip_basis)
|
||||||
new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis;
|
new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis;
|
||||||
} else {
|
else
|
||||||
new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis;
|
new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IK should not affect scale, so undo any scaling
|
// IK should not affect scale, so undo any scaling
|
||||||
new_bone_pose.basis.orthonormalize();
|
new_bone_pose.basis.orthonormalize();
|
||||||
|
@ -362,7 +324,7 @@ void FabrikInverseKinematic::_update_chain(const Skeleton *p_sk, ChainItem *p_ch
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone);
|
p_chain_item->initial_transform = p_sk->get_bone_global_pose_no_override(p_chain_item->bone);
|
||||||
p_chain_item->current_pos = p_chain_item->initial_transform.origin;
|
p_chain_item->current_pos = p_chain_item->initial_transform.origin;
|
||||||
|
|
||||||
ChainItem *items = p_chain_item->children.ptrw();
|
ChainItem *items = p_chain_item->children.ptrw();
|
||||||
|
|
Loading…
Reference in a new issue