Skip to content

Commit 464253e

Browse files
committed
Merge pull request #101571 from TokageItLab/validate-rotation-axis-spring-bone
Add validation for rotation axis to SpringBoneSimulator3D
2 parents 6820cce + 48e74af commit 464253e

4 files changed

+43
-0
lines changed

doc/classes/SpringBoneCollision3D.xml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
A collision can be a child of [SpringBoneSimulator3D]. If it is not a child of [SpringBoneSimulator3D], it has no effect.
88
The colliding and sliding are done in the [SpringBoneSimulator3D]'s modification process in order of its collision list which is set by [method SpringBoneSimulator3D.set_collision_path]. If [method SpringBoneSimulator3D.are_all_child_collisions_enabled] is [code]true[/code], the order matches [SceneTree].
99
If [member bone] is set, it synchronizes with the bone pose of the ancestor [Skeleton3D], which is done in before the [SpringBoneSimulator3D]'s modification process as the pre-process.
10+
[b]Warning:[/b] A scaled [SpringBoneCollision3D] will likely not behave as expected. Make sure that the parent [Skeleton3D] and its bones are not scaled.
1011
</description>
1112
<tutorials>
1213
</tutorials>

doc/classes/SpringBoneSimulator3D.xml

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Several properties can be applied to each joint, such as [method set_joint_stiffness], [method set_joint_drag], and [method set_joint_gravity].
1111
For simplicity, you can set values to all joints at the same time by using a [Curve]. If you want to specify detailed values individually, set [method set_individual_config] to [code]true[/code].
1212
For physical simulation, [SpringBoneSimulator3D] can have children as self-standing collisions that are not related to [PhysicsServer3D], see also [SpringBoneCollision3D].
13+
[b]Warning:[/b] A scaled [SpringBoneSimulator3D] will likely not behave as expected. Make sure that the parent [Skeleton3D] and its bones are not scaled.
1314
</description>
1415
<tutorials>
1516
</tutorials>
@@ -570,6 +571,7 @@
570571
<description>
571572
Sets the rotation axis of the bone chain. If sets a specific axis, it acts like a hinge joint.
572573
The value is cached in each joint setting in the joint list.
574+
[b]Note:[/b] The rotation axis and the forward vector shouldn't be colinear to avoid unintended rotation since [SpringBoneSimulator3D] does not factor in twisting forces.
573575
</description>
574576
</method>
575577
<method name="set_stiffness">

scene/3d/spring_bone_simulator_3d.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,10 @@ void SpringBoneSimulator3D::set_joint_rotation_axis(int p_index, int p_joint, Ro
926926
Vector<SpringBone3DJointSetting *> &joints = settings[p_index]->joints;
927927
ERR_FAIL_INDEX(p_joint, joints.size());
928928
joints[p_joint]->rotation_axis = p_axis;
929+
Skeleton3D *sk = get_skeleton();
930+
if (sk) {
931+
_validate_rotation_axis(sk, p_index, p_joint);
932+
}
929933
}
930934

931935
SpringBoneSimulator3D::RotationAxis SpringBoneSimulator3D::get_joint_rotation_axis(int p_index, int p_joint) const {
@@ -1237,6 +1241,35 @@ void SpringBoneSimulator3D::remove_child_notify(Node *p_child) {
12371241
}
12381242
}
12391243

1244+
void SpringBoneSimulator3D::_validate_rotation_axes(Skeleton3D *p_skeleton) const {
1245+
for (int i = 0; i < settings.size(); i++) {
1246+
for (int j = 0; j < settings[i]->joints.size(); j++) {
1247+
_validate_rotation_axis(p_skeleton, i, j);
1248+
}
1249+
}
1250+
}
1251+
1252+
void SpringBoneSimulator3D::_validate_rotation_axis(Skeleton3D *p_skeleton, int p_index, int p_joint) const {
1253+
RotationAxis axis = settings[p_index]->joints[p_joint]->rotation_axis;
1254+
if (axis == ROTATION_AXIS_ALL) {
1255+
return;
1256+
}
1257+
Vector3 rot = get_vector_from_axis(static_cast<Vector3::Axis>((int)axis));
1258+
Vector3 fwd;
1259+
if (p_joint < settings[p_index]->joints.size() - 1) {
1260+
fwd = p_skeleton->get_bone_rest(settings[p_index]->joints[p_joint + 1]->bone).origin;
1261+
} else if (settings[p_index]->extend_end_bone) {
1262+
fwd = get_end_bone_axis(settings[p_index]->end_bone, settings[p_index]->end_bone_direction);
1263+
if (fwd.is_zero_approx()) {
1264+
return;
1265+
}
1266+
}
1267+
fwd.normalize();
1268+
if (Math::is_equal_approx(Math::absf(rot.dot(fwd)), 1.0f)) {
1269+
WARN_PRINT_ED("Setting: " + itos(p_index) + " Joint: " + itos(p_joint) + ": Rotation axis and forward vectors are colinear. This is not advised as it may cause unwanted rotation.");
1270+
}
1271+
}
1272+
12401273
void SpringBoneSimulator3D::_find_collisions() {
12411274
if (!collisions_dirty) {
12421275
return;
@@ -1407,6 +1440,10 @@ void SpringBoneSimulator3D::_update_joints() {
14071440
settings[i]->joints_dirty = false;
14081441
}
14091442
joints_dirty = false;
1443+
Skeleton3D *sk = get_skeleton();
1444+
if (sk) {
1445+
_validate_rotation_axes(sk);
1446+
}
14101447
#ifdef TOOLS_ENABLED
14111448
update_gizmos();
14121449
#endif // TOOLS_ENABLED

scene/3d/spring_bone_simulator_3d.h

+3
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ class SpringBoneSimulator3D : public SkeletonModifier3D {
163163
virtual void move_child_notify(Node *p_child) override;
164164
virtual void remove_child_notify(Node *p_child) override;
165165

166+
void _validate_rotation_axes(Skeleton3D *p_skeleton) const;
167+
void _validate_rotation_axis(Skeleton3D *p_skeleton, int p_index, int p_joint) const;
168+
166169
public:
167170
// Setting.
168171
void set_root_bone_name(int p_index, const String &p_bone_name);

0 commit comments

Comments
 (0)