Skip to content

Commit de2cd66

Browse files
committed
Merge pull request godotengine#100463 from PiCode9560/softbody-apply-force-and-impulse
Add ability to apply forces and impulses to `SoftBody3D`
2 parents 955744f + fe3aaa2 commit de2cd66

19 files changed

+303
-0
lines changed

doc/classes/PhysicsServer3D.xml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,42 @@
10581058
Adds the given body to the list of bodies exempt from collisions.
10591059
</description>
10601060
</method>
1061+
<method name="soft_body_apply_central_force">
1062+
<return type="void" />
1063+
<param index="0" name="body" type="RID" />
1064+
<param index="1" name="force" type="Vector3" />
1065+
<description>
1066+
Distributes and applies a force to all points. A force is time dependent and meant to be applied every physics update.
1067+
</description>
1068+
</method>
1069+
<method name="soft_body_apply_central_impulse">
1070+
<return type="void" />
1071+
<param index="0" name="body" type="RID" />
1072+
<param index="1" name="impulse" type="Vector3" />
1073+
<description>
1074+
Distributes and applies an impulse to all points.
1075+
An impulse is time-independent! Applying an impulse every frame would result in a framerate-dependent force. For this reason, it should only be used when simulating one-time impacts (use the "_force" functions otherwise).
1076+
</description>
1077+
</method>
1078+
<method name="soft_body_apply_point_force">
1079+
<return type="void" />
1080+
<param index="0" name="body" type="RID" />
1081+
<param index="1" name="point_index" type="int" />
1082+
<param index="2" name="force" type="Vector3" />
1083+
<description>
1084+
Applies a force to a point. A force is time dependent and meant to be applied every physics update.
1085+
</description>
1086+
</method>
1087+
<method name="soft_body_apply_point_impulse">
1088+
<return type="void" />
1089+
<param index="0" name="body" type="RID" />
1090+
<param index="1" name="point_index" type="int" />
1091+
<param index="2" name="impulse" type="Vector3" />
1092+
<description>
1093+
Applies an impulse to a point.
1094+
An impulse is time-independent! Applying an impulse every frame would result in a framerate-dependent force. For this reason, it should only be used when simulating one-time impacts (use the "_force" functions otherwise).
1095+
</description>
1096+
</method>
10611097
<method name="soft_body_create">
10621098
<return type="RID" />
10631099
<description>

doc/classes/PhysicsServer3DExtension.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,36 @@
989989
<description>
990990
</description>
991991
</method>
992+
<method name="_soft_body_apply_central_force" qualifiers="virtual">
993+
<return type="void" />
994+
<param index="0" name="body" type="RID" />
995+
<param index="1" name="force" type="Vector3" />
996+
<description>
997+
</description>
998+
</method>
999+
<method name="_soft_body_apply_central_impulse" qualifiers="virtual">
1000+
<return type="void" />
1001+
<param index="0" name="body" type="RID" />
1002+
<param index="1" name="impulse" type="Vector3" />
1003+
<description>
1004+
</description>
1005+
</method>
1006+
<method name="_soft_body_apply_point_force" qualifiers="virtual">
1007+
<return type="void" />
1008+
<param index="0" name="body" type="RID" />
1009+
<param index="1" name="point_index" type="int" />
1010+
<param index="2" name="force" type="Vector3" />
1011+
<description>
1012+
</description>
1013+
</method>
1014+
<method name="_soft_body_apply_point_impulse" qualifiers="virtual">
1015+
<return type="void" />
1016+
<param index="0" name="body" type="RID" />
1017+
<param index="1" name="point_index" type="int" />
1018+
<param index="2" name="impulse" type="Vector3" />
1019+
<description>
1020+
</description>
1021+
</method>
9921022
<method name="_soft_body_create" qualifiers="virtual">
9931023
<return type="RID" />
9941024
<description>

doc/classes/SoftBody3D.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,38 @@
1919
Adds a body to the list of bodies that this body can't collide with.
2020
</description>
2121
</method>
22+
<method name="apply_central_force">
23+
<return type="void" />
24+
<param index="0" name="force" type="Vector3" />
25+
<description>
26+
Distributes and applies a force to all points. A force is time dependent and meant to be applied every physics update.
27+
</description>
28+
</method>
29+
<method name="apply_central_impulse">
30+
<return type="void" />
31+
<param index="0" name="impulse" type="Vector3" />
32+
<description>
33+
Distributes and applies an impulse to all points.
34+
An impulse is time-independent! Applying an impulse every frame would result in a framerate-dependent force. For this reason, it should only be used when simulating one-time impacts (use the "_force" functions otherwise).
35+
</description>
36+
</method>
37+
<method name="apply_force">
38+
<return type="void" />
39+
<param index="0" name="point_index" type="int" />
40+
<param index="1" name="force" type="Vector3" />
41+
<description>
42+
Applies a force to a point. A force is time dependent and meant to be applied every physics update.
43+
</description>
44+
</method>
45+
<method name="apply_impulse">
46+
<return type="void" />
47+
<param index="0" name="point_index" type="int" />
48+
<param index="1" name="impulse" type="Vector3" />
49+
<description>
50+
Applies an impulse to a point.
51+
An impulse is time-independent! Applying an impulse every frame would result in a framerate-dependent force. For this reason, it should only be used when simulating one-time impacts (use the "_force" functions otherwise).
52+
</description>
53+
</method>
2254
<method name="get_collision_exceptions">
2355
<return type="PhysicsBody3D[]" />
2456
<description>

modules/godot_physics_3d/godot_physics_server_3d.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,34 @@ void GodotPhysicsServer3D::body_apply_force(RID p_body, const Vector3 &p_force,
737737
body->wakeup();
738738
}
739739

740+
void GodotPhysicsServer3D::soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) {
741+
GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body);
742+
ERR_FAIL_NULL(soft_body);
743+
744+
soft_body->apply_node_impulse(p_point_index, p_impulse);
745+
}
746+
747+
void GodotPhysicsServer3D::soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) {
748+
GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body);
749+
ERR_FAIL_NULL(soft_body);
750+
751+
soft_body->apply_node_force(p_point_index, p_force);
752+
}
753+
754+
void GodotPhysicsServer3D::soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) {
755+
GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body);
756+
ERR_FAIL_NULL(soft_body);
757+
758+
soft_body->apply_central_impulse(p_impulse);
759+
}
760+
761+
void GodotPhysicsServer3D::soft_body_apply_central_force(RID p_body, const Vector3 &p_force) {
762+
GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body);
763+
ERR_FAIL_NULL(soft_body);
764+
765+
soft_body->apply_central_force(p_force);
766+
}
767+
740768
void GodotPhysicsServer3D::body_apply_torque(RID p_body, const Vector3 &p_torque) {
741769
GodotBody3D *body = body_owner.get_or_null(p_body);
742770
ERR_FAIL_NULL(body);

modules/godot_physics_3d/godot_physics_server_3d.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ class GodotPhysicsServer3D : public PhysicsServer3D {
212212
virtual void body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override;
213213
virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) override;
214214

215+
virtual void soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) override;
216+
virtual void soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) override;
217+
virtual void soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override;
218+
virtual void soft_body_apply_central_force(RID p_body, const Vector3 &p_force) override;
219+
215220
virtual void body_apply_central_force(RID p_body, const Vector3 &p_force) override;
216221
virtual void body_apply_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position = Vector3()) override;
217222
virtual void body_apply_torque(RID p_body, const Vector3 &p_torque) override;

modules/godot_physics_3d/godot_soft_body_3d.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,30 @@ void GodotSoftBody3D::apply_node_impulse(uint32_t p_node_index, const Vector3 &p
446446
node.v += p_impulse * node.im;
447447
}
448448

449+
void GodotSoftBody3D::apply_node_force(uint32_t p_node_index, const Vector3 &p_force) {
450+
ERR_FAIL_UNSIGNED_INDEX(p_node_index, nodes.size());
451+
Node &node = nodes[p_node_index];
452+
node.f += p_force;
453+
}
454+
455+
void GodotSoftBody3D::apply_central_impulse(const Vector3 &p_impulse) {
456+
const Vector3 impulse = p_impulse / nodes.size();
457+
for (Node &node : nodes) {
458+
if (node.im > 0) {
459+
node.v += impulse * node.im;
460+
}
461+
}
462+
}
463+
464+
void GodotSoftBody3D::apply_central_force(const Vector3 &p_force) {
465+
const Vector3 force = p_force / nodes.size();
466+
for (Node &node : nodes) {
467+
if (node.im > 0) {
468+
node.f += force;
469+
}
470+
}
471+
}
472+
449473
void GodotSoftBody3D::apply_node_bias_impulse(uint32_t p_node_index, const Vector3 &p_impulse) {
450474
ERR_FAIL_UNSIGNED_INDEX(p_node_index, nodes.size());
451475
Node &node = nodes[p_node_index];

modules/godot_physics_3d/godot_soft_body_3d.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ class GodotSoftBody3D : public GodotCollisionObject3D {
172172
Vector3 get_node_velocity(uint32_t p_node_index) const;
173173
Vector3 get_node_biased_velocity(uint32_t p_node_index) const;
174174
void apply_node_impulse(uint32_t p_node_index, const Vector3 &p_impulse);
175+
void apply_node_force(uint32_t p_node_index, const Vector3 &p_force);
176+
void apply_central_impulse(const Vector3 &p_impulse);
177+
void apply_central_force(const Vector3 &p_force);
175178
void apply_node_bias_impulse(uint32_t p_node_index, const Vector3 &p_impulse);
176179

177180
uint32_t get_face_count() const;

modules/jolt_physics/jolt_physics_server_3d.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,34 @@ void JoltPhysicsServer3D::area_set_space(RID p_area, RID p_space) {
288288
area->set_space(space);
289289
}
290290

291+
void JoltPhysicsServer3D::soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) {
292+
JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
293+
ERR_FAIL_NULL(body);
294+
295+
body->apply_vertex_impulse(p_point_index, p_impulse);
296+
}
297+
298+
void JoltPhysicsServer3D::soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) {
299+
JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
300+
ERR_FAIL_NULL(body);
301+
302+
body->apply_vertex_force(p_point_index, p_force);
303+
}
304+
305+
void JoltPhysicsServer3D::soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) {
306+
JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
307+
ERR_FAIL_NULL(body);
308+
309+
body->apply_central_impulse(p_impulse);
310+
}
311+
312+
void JoltPhysicsServer3D::soft_body_apply_central_force(RID p_body, const Vector3 &p_force) {
313+
JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
314+
ERR_FAIL_NULL(body);
315+
316+
body->apply_central_force(p_force);
317+
}
318+
291319
RID JoltPhysicsServer3D::area_get_space(RID p_area) const {
292320
const JoltArea3D *area = area_owner.get_or_null(p_area);
293321
ERR_FAIL_NULL_V(area, RID());

modules/jolt_physics/jolt_physics_server_3d.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ class JoltPhysicsServer3D final : public PhysicsServer3D {
322322

323323
virtual void soft_body_set_transform(RID p_body, const Transform3D &p_transform) override;
324324

325+
virtual void soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) override;
326+
virtual void soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) override;
327+
virtual void soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override;
328+
virtual void soft_body_apply_central_force(RID p_body, const Vector3 &p_force) override;
329+
325330
virtual void soft_body_set_simulation_precision(RID p_body, int p_precision) override;
326331
virtual int soft_body_get_simulation_precision(RID p_body) const override;
327332

modules/jolt_physics/objects/jolt_soft_body_3d.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,57 @@ bool JoltSoftBody3D::is_sleeping() const {
391391
}
392392
}
393393

394+
void JoltSoftBody3D::apply_vertex_impulse(int p_index, const Vector3 &p_impulse) {
395+
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply impulse to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
396+
397+
ERR_FAIL_NULL(shared);
398+
ERR_FAIL_INDEX(p_index, (int)shared->mesh_to_physics.size());
399+
const size_t physics_index = (size_t)shared->mesh_to_physics[p_index];
400+
ERR_FAIL_COND_MSG(pinned_vertices.has(physics_index), vformat("Failed to apply impulse to point at index %d for '%s'. Point was found to be pinned.", static_cast<int>(physics_index), to_string()));
401+
402+
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
403+
404+
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
405+
JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];
406+
407+
physics_vertex.mVelocity += to_jolt(p_impulse) * physics_vertex.mInvMass;
408+
409+
wake_up();
410+
}
411+
412+
void JoltSoftBody3D::apply_vertex_force(int p_index, const Vector3 &p_force) {
413+
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply force to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
414+
415+
apply_vertex_impulse(p_index, p_force * space->get_last_step());
416+
}
417+
418+
void JoltSoftBody3D::apply_central_impulse(const Vector3 &p_impulse) {
419+
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply central impulse to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
420+
ERR_FAIL_NULL(shared);
421+
422+
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*jolt_body->GetMotionPropertiesUnchecked());
423+
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
424+
425+
const JPH::Vec3 impulse = to_jolt(p_impulse) / physics_vertices.size();
426+
427+
for (JPH::SoftBodyVertex &physics_vertex : physics_vertices) {
428+
if (physics_vertex.mInvMass > 0.0f) {
429+
physics_vertex.mVelocity += impulse * physics_vertex.mInvMass;
430+
}
431+
}
432+
433+
wake_up();
434+
}
435+
436+
void JoltSoftBody3D::apply_central_force(const Vector3 &p_force) {
437+
ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to apply central force to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
438+
ERR_FAIL_NULL(shared);
439+
440+
JPH::BodyInterface &body_iface = space->get_body_iface();
441+
442+
body_iface.AddForce(jolt_body->GetID(), to_jolt(p_force), JPH::EActivation::Activate);
443+
}
444+
394445
void JoltSoftBody3D::set_is_sleeping(bool p_enabled) {
395446
if (!in_space()) {
396447
return;

0 commit comments

Comments
 (0)