Skip to content

Commit 64bfdcf

Browse files
temptetmp2
1 parent 944a3c6 commit 64bfdcf

14 files changed

Lines changed: 508 additions & 64 deletions

doc/classes/SpringBoneCollision3D.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,15 @@
3434
The offset of the rotation from [Skeleton3D]'s [member bone] pose rotation.
3535
</member>
3636
</members>
37+
<constants>
38+
<constant name="COLLIDE_MODE_JOINT" value="0" enum="CollideMode">
39+
Collision is only with spheres around the joints.
40+
</constant>
41+
<constant name="COLLIDE_MODE_INSIDE" value="1" enum="CollideMode">
42+
Collision acts to trap the spheres around the joints within the collision.
43+
</constant>
44+
<constant name="COLLIDE_MODE_CHAIN" value="2" enum="CollideMode">
45+
Collision is against the joint spheres and the tapered cones between them.
46+
</constant>
47+
</constants>
3748
</class>

doc/classes/SpringBoneCollisionCapsule3D.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
<tutorials>
1010
</tutorials>
1111
<members>
12+
<member name="collide_mode" type="int" enum="SpringBoneCollision3D.CollideMode" setter="set_collide_mode" getter="get_collide_mode" default="SpringBoneCollision3D.COLLIDE_MODE_JOINT">
13+
Sets whether the collision capcule collides or traps the joint spheres, or interacts with the tapered cones between them.
14+
</member>
1215
<member name="height" type="float" setter="set_height" getter="get_height" default="0.5">
1316
The capsule's full height, including the hemispheres.
1417
[b]Note:[/b] The [member height] of a capsule must be at least twice its [member radius]. Otherwise, the capsule becomes a sphere. If the [member height] is less than twice the [member radius], the properties adjust to a valid value.

doc/classes/SpringBoneCollisionSphere3D.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
<tutorials>
1010
</tutorials>
1111
<members>
12-
<member name="inside" type="bool" setter="set_inside" getter="is_inside" default="false">
13-
If [code]true[/code], the collision acts to trap the joint within the collision.
12+
<member name="collide_mode" type="int" enum="SpringBoneCollision3D.CollideMode" setter="set_collide_mode" getter="get_collide_mode" default="SpringBoneCollision3D.COLLIDE_MODE_JOINT">
13+
Sets whether the collision sphere collides or traps the joint spheres, or interacts with the tapered cones between them.
1414
</member>
1515
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.1">
1616
The sphere's radius.

editor/scene/3d/gizmos/spring_bone_3d_gizmo_plugin.cpp

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,10 @@ void SpringBoneSimulator3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
111111
p_gizmo->add_mesh(mesh, Ref<Material>(), skel_tr, skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));
112112
}
113113

114+
bool draw_as_chain = true;
114115
Ref<ArrayMesh> SpringBoneSimulator3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_skeleton, SpringBoneSimulator3D *p_simulator, bool p_is_selected) {
115116
Color bone_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/spring_bone_joint");
117+
Color cone_color = Color(1, 0, 0, 1);
116118

117119
Ref<SurfaceTool> surface_tool;
118120
surface_tool.instantiate();
@@ -147,7 +149,13 @@ Ref<ArrayMesh> SpringBoneSimulator3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_s
147149
Transform3D parent_global_pose = p_skeleton->get_bone_global_rest(prev_bone);
148150
Vector3 bone_vector = p_simulator->get_bone_vector(i, j - 1);
149151
Vector3 center = parent_global_pose.translated_local(bone_vector).origin;
150-
draw_line(surface_tool, parent_global_pose.origin, center, bone_color);
152+
153+
if (draw_as_chain) {
154+
draw_sphere_tangent_cone(surface_tool, parent_global_pose.origin, p_simulator->get_joint_radius(i, MAX(0, j - 2)), center, p_simulator->get_joint_radius(i, j - 1), cone_color);
155+
} else {
156+
draw_line(surface_tool, parent_global_pose.origin, center, bone_color);
157+
}
158+
151159
draw_sphere(surface_tool, global_pose.basis, center, p_simulator->get_joint_radius(i, j - 1), bone_color);
152160

153161
// Draw rotation axis vector if not ROTATION_AXIS_ALL.
@@ -172,7 +180,11 @@ Ref<ArrayMesh> SpringBoneSimulator3DGizmoPlugin::get_joints_mesh(Skeleton3D *p_s
172180
surface_tool->set_bones(Vector<int>(bones));
173181
surface_tool->set_weights(Vector<float>(weights));
174182
Vector3 center = global_pose.translated_local(bone_vector).origin;
175-
draw_line(surface_tool, global_pose.origin, center, bone_color);
183+
if (draw_as_chain) {
184+
draw_sphere_tangent_cone(surface_tool, global_pose.origin, p_simulator->get_joint_radius(i, j - 1), center, p_simulator->get_joint_radius(i, j), cone_color);
185+
} else {
186+
draw_line(surface_tool, global_pose.origin, center, bone_color);
187+
}
176188
draw_sphere(surface_tool, global_pose.basis, center, p_simulator->get_joint_radius(i, j), bone_color);
177189
} else {
178190
bones[0] = current_bone;
@@ -222,6 +234,64 @@ void SpringBoneSimulator3DGizmoPlugin::draw_sphere(Ref<SurfaceTool> &p_surface_t
222234
}
223235
}
224236

237+
void SpringBoneSimulator3DGizmoPlugin::draw_sphere_tangent_cone(Ref<SurfaceTool> &p_surface_tool, const Vector3 &p_prev_center, float p_prev_radius, const Vector3 &p_center, float p_radius, const Color &p_color) {
238+
static constexpr int STEP = 16;
239+
static constexpr float SPPI = Math::TAU / (float)STEP;
240+
241+
Vector3 axis_vec = p_center - p_prev_center;
242+
float axis_length = axis_vec.length();
243+
if (Math::is_zero_approx(axis_length)) {
244+
return;
245+
}
246+
Vector3 cone_axis = axis_vec / axis_length;
247+
float cone_gradient = (p_prev_radius - p_radius) / axis_length;
248+
if ((cone_gradient <= -1.0) || (cone_gradient >= 1.0)) {
249+
return;
250+
}
251+
252+
Vector3 prev_cone_center = p_prev_center + cone_axis * (p_prev_radius * cone_gradient);
253+
Vector3 cone_center = p_center + cone_axis * (p_radius * cone_gradient);
254+
255+
float cone_rad_fac = sqrt(1 - cone_gradient * cone_gradient);
256+
Vector3 cone_side_axis = cone_axis.cross(fabs(axis_vec.x) < 0.5 ? Vector3(1, 0, 0) : Vector3(0, 0, 1)).normalized();
257+
Vector3 prev_cone_side_vec = cone_side_axis * (p_prev_radius * cone_rad_fac);
258+
Vector3 cone_side_vec = cone_side_axis * (p_radius * cone_rad_fac);
259+
260+
for (int i = 1; i <= STEP; i++) {
261+
p_surface_tool->set_color(p_color);
262+
p_surface_tool->add_vertex(prev_cone_center + prev_cone_side_vec.rotated(cone_axis, SPPI * ((i - 1) % STEP)));
263+
p_surface_tool->set_color(p_color);
264+
p_surface_tool->add_vertex(prev_cone_center + prev_cone_side_vec.rotated(cone_axis, SPPI * (i % STEP)));
265+
}
266+
267+
for (int i = 1; i <= STEP; i++) {
268+
p_surface_tool->set_color(p_color);
269+
p_surface_tool->add_vertex(cone_center + cone_side_vec.rotated(cone_axis, SPPI * ((i - 1) % STEP)));
270+
p_surface_tool->set_color(p_color);
271+
p_surface_tool->add_vertex(cone_center + cone_side_vec.rotated(cone_axis, SPPI * (i % STEP)));
272+
}
273+
274+
p_surface_tool->set_color(p_color);
275+
p_surface_tool->add_vertex(prev_cone_center + prev_cone_side_vec);
276+
p_surface_tool->set_color(p_color);
277+
p_surface_tool->add_vertex(cone_center + cone_side_vec);
278+
279+
p_surface_tool->set_color(p_color);
280+
p_surface_tool->add_vertex(prev_cone_center + prev_cone_side_vec.rotated(cone_axis, Math::PI * 0.5));
281+
p_surface_tool->set_color(p_color);
282+
p_surface_tool->add_vertex(cone_center + cone_side_vec.rotated(cone_axis, Math::PI * 0.5));
283+
284+
p_surface_tool->set_color(p_color);
285+
p_surface_tool->add_vertex(prev_cone_center + prev_cone_side_vec.rotated(cone_axis, Math::PI));
286+
p_surface_tool->set_color(p_color);
287+
p_surface_tool->add_vertex(cone_center + cone_side_vec.rotated(cone_axis, Math::PI));
288+
289+
p_surface_tool->set_color(p_color);
290+
p_surface_tool->add_vertex(prev_cone_center + prev_cone_side_vec.rotated(cone_axis, Math::PI * 1.5));
291+
p_surface_tool->set_color(p_color);
292+
p_surface_tool->add_vertex(cone_center + cone_side_vec.rotated(cone_axis, Math::PI * 1.5));
293+
}
294+
225295
void SpringBoneSimulator3DGizmoPlugin::draw_line(Ref<SurfaceTool> &p_surface_tool, const Vector3 &p_begin_pos, const Vector3 &p_end_pos, const Color &p_color) {
226296
p_surface_tool->set_color(p_color);
227297
p_surface_tool->add_vertex(p_begin_pos);
@@ -309,13 +379,13 @@ Ref<ArrayMesh> SpringBoneCollision3DGizmoPlugin::get_collision_mesh(SpringBoneCo
309379

310380
SpringBoneCollisionSphere3D *sphere = Object::cast_to<SpringBoneCollisionSphere3D>(p_collision);
311381
if (sphere) {
312-
draw_sphere(surface_tool, sphere->get_radius(), sphere->is_inside() ? inside_collision_color : collision_color);
382+
draw_sphere(surface_tool, sphere->get_radius(), sphere->get_collide_mode() == SpringBoneCollision3D::COLLIDE_MODE_INSIDE ? inside_collision_color : collision_color);
313383
return surface_tool->commit();
314384
}
315385

316386
SpringBoneCollisionCapsule3D *capsule = Object::cast_to<SpringBoneCollisionCapsule3D>(p_collision);
317387
if (capsule) {
318-
draw_capsule(surface_tool, capsule->get_radius(), capsule->get_height(), capsule->is_inside() ? inside_collision_color : collision_color);
388+
draw_capsule(surface_tool, capsule->get_radius(), capsule->get_height(), capsule->get_collide_mode() == SpringBoneCollision3D::COLLIDE_MODE_INSIDE ? inside_collision_color : collision_color);
319389
return surface_tool->commit();
320390
}
321391

editor/scene/3d/gizmos/spring_bone_3d_gizmo_plugin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class SpringBoneSimulator3DGizmoPlugin : public EditorNode3DGizmoPlugin {
4848
public:
4949
static Ref<ArrayMesh> get_joints_mesh(Skeleton3D *p_skeleton, SpringBoneSimulator3D *p_simulator, bool p_is_selected);
5050
static void draw_sphere(Ref<SurfaceTool> &p_surface_tool, const Basis &p_basis, const Vector3 &p_center, float p_radius, const Color &p_color);
51+
static void draw_sphere_tangent_cone(Ref<SurfaceTool> &p_surface_tool, const Vector3 &p_prev_center, float p__prev_radius, const Vector3 &p_center, float p_radius, const Color &p_color);
5152
static void draw_line(Ref<SurfaceTool> &p_surface_tool, const Vector3 &p_begin_pos, const Vector3 &p_end_pos, const Color &p_color);
5253

5354
bool has_gizmo(Node3D *p_spatial) override;

scene/3d/spring_bone_collision_3d.cpp

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ void SpringBoneCollision3D::set_bone(int p_bone) {
9999

100100
Skeleton3D *sk = get_skeleton();
101101
if (sk) {
102-
if (bone <= -1 || bone >= sk->get_bone_count()) {
102+
if (bone < -1 || bone >= sk->get_bone_count()) {
103103
WARN_PRINT("Bone index '" + itos(p_bone) + "' is out of range! Cannot connect BoneAttachment to node!");
104104
bone = -1;
105105
} else {
@@ -187,6 +187,10 @@ void SpringBoneCollision3D::_bind_methods() {
187187
ADD_GROUP("Offset", "");
188188
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position_offset"), "set_position_offset", "get_position_offset");
189189
ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "rotation_offset"), "set_rotation_offset", "get_rotation_offset");
190+
191+
BIND_ENUM_CONSTANT(COLLIDE_MODE_JOINT);
192+
BIND_ENUM_CONSTANT(COLLIDE_MODE_INSIDE);
193+
BIND_ENUM_CONSTANT(COLLIDE_MODE_CHAIN);
190194
}
191195

192196
void SpringBoneCollision3D::_notification(int p_what) {
@@ -198,11 +202,113 @@ void SpringBoneCollision3D::_notification(int p_what) {
198202
}
199203
}
200204

201-
Vector3 SpringBoneCollision3D::collide(const Transform3D &p_center, float p_bone_radius, float p_bone_length, const Vector3 &p_current) const {
202-
return _collide(p_center, p_bone_radius, p_bone_length, p_current);
205+
int SpringBoneCollision3D::Dsegmentindexbeingcalculated = 0;
206+
207+
// static
208+
Vector3 SpringBoneCollision3D::_collide_sphere(const Vector3 &p_origin, float p_radius, bool p_inside, float p_bone_radius, const Vector3 &p_current) {
209+
Vector3 diff = p_current - p_origin;
210+
float diff_length = diff.length();
211+
float r = p_inside ? p_radius - p_bone_radius : p_bone_radius + p_radius;
212+
float distance = p_inside ? r - diff_length : diff_length - r;
213+
if (distance > 0) {
214+
return p_current;
215+
}
216+
return p_origin + diff.normalized() * r;
217+
}
218+
219+
// static
220+
Vector3 SpringBoneCollision3D::_collide_sphere_taper(const Vector3 &p_origin, float p_radius, float p_bone_radius, float p_bone_length, const Vector3 &p_current_origin, float p_bone_origin_radius, const Vector3 &p_current) {
221+
// (p_origin, p_radius) defines the external collider
222+
// The bone capsule is from (p_current_origin, p_bone_origin_radius) to (p_current, p_bone_radius)
223+
// where p_current is to be displaced
224+
225+
float taper_fore = (p_bone_origin_radius - p_bone_radius) / p_bone_length;
226+
227+
// send the short taper case into old implementation
228+
if (Math::abs(taper_fore) >= 1.0) {
229+
return _collide_sphere(p_origin, p_radius, false, p_bone_radius, p_current);
230+
}
231+
232+
Vector3 diff = p_current - p_origin;
233+
Vector3 bone_axis = p_current - p_current_origin;
234+
DEV_ASSERT(Math::is_equal_approx(bone_axis.length(), p_bone_length));
235+
float taper_side = Math::sqrt(1.0 - taper_fore * taper_fore);
236+
float bone_axis_sq = bone_axis.dot(bone_axis);
237+
float lam = 1.0 - bone_axis.dot(diff) / bone_axis_sq; // calculated from the tail end
238+
Vector3 vecside = p_origin - (p_current_origin + bone_axis * lam);
239+
// printf(" zz=%f ", vecside.dot(bone_axis)); // should be zero
240+
float radial_distance = vecside.length();
241+
if (radial_distance > MAX(p_bone_origin_radius, p_bone_radius) + p_radius) {
242+
return p_current;
243+
}
244+
float bone_axis_length = Math::sqrt(bone_axis_sq);
245+
246+
// limit contact with the cone close to the root where it gets twitchy
247+
float gapdistance = p_bone_radius * 0.5 + p_bone_origin_radius * 0.5 + p_radius * Math::sqrt(0.5);
248+
float lamconemin = gapdistance / bone_axis_length * 0.5;
249+
250+
// case of collide sphere being very large.
251+
if (lamconemin > 1.0) { // apply this case before the beyond origin end to avoid twitchiness
252+
return _collide_sphere(p_origin, p_radius, false, p_bone_radius, p_current);
253+
}
254+
255+
float lamd = radial_distance * taper_fore / taper_side / bone_axis_length;
256+
float lamcone = lam - lamd;
257+
if (lamcone <= 0.0) { // beyond origin end
258+
return p_current;
259+
}
260+
if (lamcone >= 1.0) { // beyond tail end
261+
return _collide_sphere(p_origin, p_radius, false, p_bone_radius, p_current);
262+
}
263+
264+
// prove numerically this is the closest approach to the cone
265+
/*float lam1 = lamcone;
266+
float m1 = (p_current_origin + bone_axis * lam1 - p_origin).length() - (p_bone_origin_radius + (p_bone_radius - p_bone_origin_radius) * lam1);
267+
float lam0 = lamcone - 0.01;
268+
float m0 = (p_current_origin + bone_axis * lam0 - p_origin).length() - (p_bone_origin_radius + (p_bone_radius - p_bone_origin_radius) * lam0);
269+
float lam2 = lamcone + 0.01;
270+
float m2 = (p_current_origin + bone_axis * lam2 - p_origin).length() - (p_bone_origin_radius + (p_bone_radius - p_bone_origin_radius) * lam2);
271+
printf(" check %f>0 ", std::min(m0, m2) - m1);
272+
*/
273+
274+
if (lamcone < lamconemin) {
275+
lamcone = lamconemin;
276+
}
277+
278+
// Check collision with this cone
279+
Vector3 coneaxispoint = p_current_origin + bone_axis * lamcone;
280+
Vector3 conepointdiff = coneaxispoint - p_origin;
281+
float coneaxisradius = p_bone_origin_radius + (p_bone_radius - p_bone_origin_radius) * lamcone;
282+
283+
float r = coneaxisradius + p_radius;
284+
float conepointdifflength = conepointdiff.length();
285+
float distance = conepointdifflength - r;
286+
if (distance > 0.0) {
287+
return p_current;
288+
}
289+
//printf(" hh=%f; ", distance);
290+
291+
// We could model a rotation of the bone_axis about p_current_origin to move the (coneaxispoint, coneaxisradius) sphere
292+
// away from its intersection with (p_origin, p_radius) [not quite accurate since as it rotates the lamcone position
293+
// of the virtual sphere inside the cone changes].
294+
// But instead we will just project it as a simple lever and rely on limit_length() and the iteration to settle it into the correct place.
295+
296+
// position virtual sphere of contact in the cone is pushed to
297+
Vector3 p_coneaxispointnew = p_origin + conepointdiff.normalized() * r;
298+
299+
// projection out to the end point of the cone as though it were a lever
300+
// (this isn't necessary since the change it makes is masked by the limit_length() function)
301+
// also limit the size of the multiplier to avoid extreme movement when near the joint
302+
Vector3 p_current_new = p_current_origin + (p_coneaxispointnew - p_current_origin) / MAX(0.1f, lamcone);
303+
304+
return p_current_new;
305+
}
306+
307+
Vector3 SpringBoneCollision3D::collide(const Transform3D &p_center, float p_bone_radius, float p_bone_length, const Vector3 &p_current_origin, float p_bone_origin_radius, const Vector3 &p_current) const {
308+
return _collide(p_center, p_bone_radius, p_bone_length, p_current_origin, p_bone_origin_radius, p_current);
203309
}
204310

205-
Vector3 SpringBoneCollision3D::_collide(const Transform3D &p_center, float p_bone_radius, float p_bone_length, const Vector3 &p_current) const {
311+
Vector3 SpringBoneCollision3D::_collide(const Transform3D &p_center, float p_bone_radius, float p_bone_length, const Vector3 &p_current_origin, float p_bone_origin_radius, const Vector3 &p_current) const {
206312
return Vector3(0, 0, 0);
207313
}
208314

scene/3d/spring_bone_collision_3d.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,18 @@ class SpringBoneCollision3D : public Node3D {
5050
void _notification(int p_what);
5151
static void _bind_methods();
5252

53-
virtual Vector3 _collide(const Transform3D &p_center, float p_bone_radius, float p_bone_length, const Vector3 &p_current) const;
53+
static Vector3 _collide_sphere(const Vector3 &p_origin, float p_radius, bool p_inside, float p_bone_radius, const Vector3 &p_current);
54+
static Vector3 _collide_sphere_taper(const Vector3 &p_origin, float p_radius, float p_bone_radius, float p_bone_length, const Vector3 &p_current_origin, float p_bone_origin_radius, const Vector3 &p_current);
55+
56+
virtual Vector3 _collide(const Transform3D &p_center, float p_bone_radius, float p_bone_length, const Vector3 &p_current_origin, float p_bone_origin_radius, const Vector3 &p_current) const;
5457

5558
public:
59+
enum CollideMode {
60+
COLLIDE_MODE_JOINT,
61+
COLLIDE_MODE_INSIDE,
62+
COLLIDE_MODE_CHAIN,
63+
};
64+
5665
Skeleton3D *get_skeleton() const;
5766

5867
void set_bone_name(const String &p_name);
@@ -69,6 +78,9 @@ class SpringBoneCollision3D : public Node3D {
6978
Transform3D get_transform_from_skeleton(const Transform3D &p_center) const;
7079

7180
Vector3 collide(const Transform3D &p_center, float p_bone_radius, float p_bone_length, const Vector3 &p_current) const;
81+
static int Dsegmentindexbeingcalculated;
7282

7383
SpringBoneCollision3D();
7484
};
85+
86+
VARIANT_ENUM_CAST(SpringBoneCollision3D::CollideMode);

0 commit comments

Comments
 (0)