diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index f87d6342c79..f422dbb1310 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -1058,6 +1058,42 @@ Adds the given body to the list of bodies exempt from collisions. + + + + + + Distributes and applies a force to all points. A force is time dependent and meant to be applied every physics update. + + + + + + + + Distributes and applies an impulse to all points. + 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). + + + + + + + + + Applies a force to a point. A force is time dependent and meant to be applied every physics update. + + + + + + + + + Applies an impulse to a point. + 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). + + diff --git a/doc/classes/PhysicsServer3DExtension.xml b/doc/classes/PhysicsServer3DExtension.xml index e58a7ff9a8a..e6d3c576d5c 100644 --- a/doc/classes/PhysicsServer3DExtension.xml +++ b/doc/classes/PhysicsServer3DExtension.xml @@ -989,6 +989,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml index 4d8791d8c1f..241e3bb6b77 100644 --- a/doc/classes/SoftBody3D.xml +++ b/doc/classes/SoftBody3D.xml @@ -19,6 +19,38 @@ Adds a body to the list of bodies that this body can't collide with. + + + + + Distributes and applies a force to all points. A force is time dependent and meant to be applied every physics update. + + + + + + + Distributes and applies an impulse to all points. + 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). + + + + + + + + Applies a force to a point. A force is time dependent and meant to be applied every physics update. + + + + + + + + Applies an impulse to a point. + 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). + + diff --git a/modules/godot_physics_3d/godot_physics_server_3d.cpp b/modules/godot_physics_3d/godot_physics_server_3d.cpp index 35aa538ef51..1fa92c81b29 100644 --- a/modules/godot_physics_3d/godot_physics_server_3d.cpp +++ b/modules/godot_physics_3d/godot_physics_server_3d.cpp @@ -737,6 +737,34 @@ void GodotPhysicsServer3D::body_apply_force(RID p_body, const Vector3 &p_force, body->wakeup(); } +void GodotPhysicsServer3D::soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) { + GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(soft_body); + + soft_body->apply_node_impulse(p_point_index, p_impulse); +} + +void GodotPhysicsServer3D::soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) { + GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(soft_body); + + soft_body->apply_node_force(p_point_index, p_force); +} + +void GodotPhysicsServer3D::soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) { + GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(soft_body); + + soft_body->apply_central_impulse(p_impulse); +} + +void GodotPhysicsServer3D::soft_body_apply_central_force(RID p_body, const Vector3 &p_force) { + GodotSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(soft_body); + + soft_body->apply_central_force(p_force); +} + void GodotPhysicsServer3D::body_apply_torque(RID p_body, const Vector3 &p_torque) { GodotBody3D *body = body_owner.get_or_null(p_body); ERR_FAIL_NULL(body); diff --git a/modules/godot_physics_3d/godot_physics_server_3d.h b/modules/godot_physics_3d/godot_physics_server_3d.h index b394ab592b3..407a97d0ae7 100644 --- a/modules/godot_physics_3d/godot_physics_server_3d.h +++ b/modules/godot_physics_3d/godot_physics_server_3d.h @@ -212,6 +212,11 @@ public: virtual void body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override; virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) override; + virtual void soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) override; + virtual void soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) override; + virtual void soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override; + virtual void soft_body_apply_central_force(RID p_body, const Vector3 &p_force) override; + virtual void body_apply_central_force(RID p_body, const Vector3 &p_force) override; virtual void body_apply_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position = Vector3()) override; virtual void body_apply_torque(RID p_body, const Vector3 &p_torque) override; diff --git a/modules/godot_physics_3d/godot_soft_body_3d.cpp b/modules/godot_physics_3d/godot_soft_body_3d.cpp index a117095648d..3b9270b723b 100644 --- a/modules/godot_physics_3d/godot_soft_body_3d.cpp +++ b/modules/godot_physics_3d/godot_soft_body_3d.cpp @@ -446,6 +446,30 @@ void GodotSoftBody3D::apply_node_impulse(uint32_t p_node_index, const Vector3 &p node.v += p_impulse * node.im; } +void GodotSoftBody3D::apply_node_force(uint32_t p_node_index, const Vector3 &p_force) { + ERR_FAIL_UNSIGNED_INDEX(p_node_index, nodes.size()); + Node &node = nodes[p_node_index]; + node.f += p_force; +} + +void GodotSoftBody3D::apply_central_impulse(const Vector3 &p_impulse) { + const Vector3 impulse = p_impulse / nodes.size(); + for (Node &node : nodes) { + if (node.im > 0) { + node.v += impulse * node.im; + } + } +} + +void GodotSoftBody3D::apply_central_force(const Vector3 &p_force) { + const Vector3 force = p_force / nodes.size(); + for (Node &node : nodes) { + if (node.im > 0) { + node.f += force; + } + } +} + void GodotSoftBody3D::apply_node_bias_impulse(uint32_t p_node_index, const Vector3 &p_impulse) { ERR_FAIL_UNSIGNED_INDEX(p_node_index, nodes.size()); Node &node = nodes[p_node_index]; diff --git a/modules/godot_physics_3d/godot_soft_body_3d.h b/modules/godot_physics_3d/godot_soft_body_3d.h index 1fcf349fb2b..8e6f49a60de 100644 --- a/modules/godot_physics_3d/godot_soft_body_3d.h +++ b/modules/godot_physics_3d/godot_soft_body_3d.h @@ -172,6 +172,9 @@ public: Vector3 get_node_velocity(uint32_t p_node_index) const; Vector3 get_node_biased_velocity(uint32_t p_node_index) const; void apply_node_impulse(uint32_t p_node_index, const Vector3 &p_impulse); + void apply_node_force(uint32_t p_node_index, const Vector3 &p_force); + void apply_central_impulse(const Vector3 &p_impulse); + void apply_central_force(const Vector3 &p_force); void apply_node_bias_impulse(uint32_t p_node_index, const Vector3 &p_impulse); uint32_t get_face_count() const; diff --git a/modules/jolt_physics/jolt_physics_server_3d.cpp b/modules/jolt_physics/jolt_physics_server_3d.cpp index d126f8bde7b..6178ab5cf66 100644 --- a/modules/jolt_physics/jolt_physics_server_3d.cpp +++ b/modules/jolt_physics/jolt_physics_server_3d.cpp @@ -288,6 +288,34 @@ void JoltPhysicsServer3D::area_set_space(RID p_area, RID p_space) { area->set_space(space); } +void JoltPhysicsServer3D::soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) { + JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(body); + + body->apply_vertex_impulse(p_point_index, p_impulse); +} + +void JoltPhysicsServer3D::soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) { + JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(body); + + body->apply_vertex_force(p_point_index, p_force); +} + +void JoltPhysicsServer3D::soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) { + JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(body); + + body->apply_central_impulse(p_impulse); +} + +void JoltPhysicsServer3D::soft_body_apply_central_force(RID p_body, const Vector3 &p_force) { + JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body); + ERR_FAIL_NULL(body); + + body->apply_central_force(p_force); +} + RID JoltPhysicsServer3D::area_get_space(RID p_area) const { const JoltArea3D *area = area_owner.get_or_null(p_area); ERR_FAIL_NULL_V(area, RID()); diff --git a/modules/jolt_physics/jolt_physics_server_3d.h b/modules/jolt_physics/jolt_physics_server_3d.h index 5ee1439459f..3f183fedf61 100644 --- a/modules/jolt_physics/jolt_physics_server_3d.h +++ b/modules/jolt_physics/jolt_physics_server_3d.h @@ -322,6 +322,11 @@ public: virtual void soft_body_set_transform(RID p_body, const Transform3D &p_transform) override; + virtual void soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) override; + virtual void soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) override; + virtual void soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override; + virtual void soft_body_apply_central_force(RID p_body, const Vector3 &p_force) override; + virtual void soft_body_set_simulation_precision(RID p_body, int p_precision) override; virtual int soft_body_get_simulation_precision(RID p_body) const override; diff --git a/modules/jolt_physics/objects/jolt_soft_body_3d.cpp b/modules/jolt_physics/objects/jolt_soft_body_3d.cpp index 67c752748a4..3e793fc8808 100644 --- a/modules/jolt_physics/objects/jolt_soft_body_3d.cpp +++ b/modules/jolt_physics/objects/jolt_soft_body_3d.cpp @@ -396,6 +396,57 @@ bool JoltSoftBody3D::is_sleeping() const { } } +void JoltSoftBody3D::apply_vertex_impulse(int p_index, const Vector3 &p_impulse) { + 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())); + + ERR_FAIL_NULL(shared); + ERR_FAIL_INDEX(p_index, (int)shared->mesh_to_physics.size()); + const size_t physics_index = (size_t)shared->mesh_to_physics[p_index]; + 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(physics_index), to_string())); + + JPH::SoftBodyMotionProperties &motion_properties = static_cast(*jolt_body->GetMotionPropertiesUnchecked()); + + JPH::Array &physics_vertices = motion_properties.GetVertices(); + JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index]; + + physics_vertex.mVelocity += to_jolt(p_impulse) * physics_vertex.mInvMass; + + wake_up(); +} + +void JoltSoftBody3D::apply_vertex_force(int p_index, const Vector3 &p_force) { + 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())); + + apply_vertex_impulse(p_index, p_force * space->get_last_step()); +} + +void JoltSoftBody3D::apply_central_impulse(const Vector3 &p_impulse) { + 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())); + ERR_FAIL_NULL(shared); + + JPH::SoftBodyMotionProperties &motion_properties = static_cast(*jolt_body->GetMotionPropertiesUnchecked()); + JPH::Array &physics_vertices = motion_properties.GetVertices(); + + const JPH::Vec3 impulse = to_jolt(p_impulse) / physics_vertices.size(); + + for (JPH::SoftBodyVertex &physics_vertex : physics_vertices) { + if (physics_vertex.mInvMass > 0.0f) { + physics_vertex.mVelocity += impulse * physics_vertex.mInvMass; + } + } + + wake_up(); +} + +void JoltSoftBody3D::apply_central_force(const Vector3 &p_force) { + 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())); + ERR_FAIL_NULL(shared); + + JPH::BodyInterface &body_iface = space->get_body_iface(); + + body_iface.AddForce(jolt_body->GetID(), to_jolt(p_force), JPH::EActivation::Activate); +} + void JoltSoftBody3D::set_is_sleeping(bool p_enabled) { if (!in_space()) { return; diff --git a/modules/jolt_physics/objects/jolt_soft_body_3d.h b/modules/jolt_physics/objects/jolt_soft_body_3d.h index 4ec422e5553..68b19fe7cb8 100644 --- a/modules/jolt_physics/objects/jolt_soft_body_3d.h +++ b/modules/jolt_physics/objects/jolt_soft_body_3d.h @@ -166,4 +166,9 @@ public: void unpin_all_vertices(); bool is_vertex_pinned(int p_index) const; + + void apply_vertex_impulse(int p_index, const Vector3 &p_impulse); + void apply_vertex_force(int p_index, const Vector3 &p_force); + void apply_central_impulse(const Vector3 &p_impulse); + void apply_central_force(const Vector3 &p_force); }; diff --git a/scene/3d/physics/soft_body_3d.cpp b/scene/3d/physics/soft_body_3d.cpp index b0fc94d75f8..fc63836cb50 100644 --- a/scene/3d/physics/soft_body_3d.cpp +++ b/scene/3d/physics/soft_body_3d.cpp @@ -363,6 +363,11 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); + ClassDB::bind_method(D_METHOD("apply_impulse", "point_index", "impulse"), &SoftBody3D::apply_impulse); + ClassDB::bind_method(D_METHOD("apply_force", "point_index", "force"), &SoftBody3D::apply_force); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &SoftBody3D::apply_central_impulse); + ClassDB::bind_method(D_METHOD("apply_central_force", "force"), &SoftBody3D::apply_central_force); + ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path", "insert_at"), &SoftBody3D::pin_point, DEFVAL(NodePath()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned); @@ -671,6 +676,22 @@ Vector3 SoftBody3D::get_point_transform(int p_point_index) { return PhysicsServer3D::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index); } +void SoftBody3D::apply_impulse(int p_point_index, const Vector3 &p_impulse) { + PhysicsServer3D::get_singleton()->soft_body_apply_point_impulse(physics_rid, p_point_index, p_impulse); +} + +void SoftBody3D::apply_force(int p_point_index, const Vector3 &p_force) { + PhysicsServer3D::get_singleton()->soft_body_apply_point_force(physics_rid, p_point_index, p_force); +} + +void SoftBody3D::apply_central_impulse(const Vector3 &p_impulse) { + PhysicsServer3D::get_singleton()->soft_body_apply_central_impulse(physics_rid, p_impulse); +} + +void SoftBody3D::apply_central_force(const Vector3 &p_force) { + PhysicsServer3D::get_singleton()->soft_body_apply_central_force(physics_rid, p_force); +} + void SoftBody3D::pin_point_toggle(int p_point_index) { pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); } diff --git a/scene/3d/physics/soft_body_3d.h b/scene/3d/physics/soft_body_3d.h index f136b7ac0b9..722b866eaa8 100644 --- a/scene/3d/physics/soft_body_3d.h +++ b/scene/3d/physics/soft_body_3d.h @@ -188,6 +188,11 @@ public: void set_ray_pickable(bool p_ray_pickable); bool is_ray_pickable() const; + void apply_impulse(int p_point_index, const Vector3 &p_impulse); + void apply_force(int p_point_index, const Vector3 &p_force); + void apply_central_impulse(const Vector3 &p_impulse); + void apply_central_force(const Vector3 &p_force); + SoftBody3D(); ~SoftBody3D(); diff --git a/servers/extensions/physics_server_3d_extension.cpp b/servers/extensions/physics_server_3d_extension.cpp index 0937021c641..e1479967411 100644 --- a/servers/extensions/physics_server_3d_extension.cpp +++ b/servers/extensions/physics_server_3d_extension.cpp @@ -358,6 +358,11 @@ void PhysicsServer3DExtension::_bind_methods() { GDVIRTUAL_BIND(_soft_body_pin_point, "body", "point_index", "pin"); GDVIRTUAL_BIND(_soft_body_is_point_pinned, "body", "point_index"); + GDVIRTUAL_BIND(_soft_body_apply_point_impulse, "body", "point_index", "impulse"); + GDVIRTUAL_BIND(_soft_body_apply_point_force, "body", "point_index", "force"); + GDVIRTUAL_BIND(_soft_body_apply_central_impulse, "body", "impulse"); + GDVIRTUAL_BIND(_soft_body_apply_central_force, "body", "force"); + /* JOINT API */ GDVIRTUAL_BIND(_joint_create); diff --git a/servers/extensions/physics_server_3d_extension.h b/servers/extensions/physics_server_3d_extension.h index 4d27efacf7f..424e5c983a3 100644 --- a/servers/extensions/physics_server_3d_extension.h +++ b/servers/extensions/physics_server_3d_extension.h @@ -465,6 +465,11 @@ public: EXBIND3(soft_body_pin_point, RID, int, bool) EXBIND2RC(bool, soft_body_is_point_pinned, RID, int) + EXBIND3(soft_body_apply_point_impulse, RID, int, const Vector3 &) + EXBIND3(soft_body_apply_point_force, RID, int, const Vector3 &) + EXBIND2(soft_body_apply_central_impulse, RID, const Vector3 &) + EXBIND2(soft_body_apply_central_force, RID, const Vector3 &) + /* JOINT API */ EXBIND0R(RID, joint_create) diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index f85ce55d991..53aa516d572 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -894,6 +894,11 @@ void PhysicsServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("soft_body_is_point_pinned", "body", "point_index"), &PhysicsServer3D::soft_body_is_point_pinned); + ClassDB::bind_method(D_METHOD("soft_body_apply_point_impulse", "body", "point_index", "impulse"), &PhysicsServer3D::soft_body_apply_point_impulse); + ClassDB::bind_method(D_METHOD("soft_body_apply_point_force", "body", "point_index", "force"), &PhysicsServer3D::soft_body_apply_point_force); + ClassDB::bind_method(D_METHOD("soft_body_apply_central_impulse", "body", "impulse"), &PhysicsServer3D::soft_body_apply_central_impulse); + ClassDB::bind_method(D_METHOD("soft_body_apply_central_force", "body", "force"), &PhysicsServer3D::soft_body_apply_central_force); + /* JOINT API */ ClassDB::bind_method(D_METHOD("joint_create"), &PhysicsServer3D::joint_create); diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 921b590ce61..da0d0a36045 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -619,6 +619,11 @@ public: virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) = 0; virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) const = 0; + virtual void soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) = 0; + virtual void soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) = 0; + virtual void soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) = 0; + virtual void soft_body_apply_central_force(RID p_body, const Vector3 &p_force) = 0; + virtual void soft_body_remove_all_pinned_points(RID p_body) = 0; virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) = 0; virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) const = 0; diff --git a/servers/physics_server_3d_dummy.h b/servers/physics_server_3d_dummy.h index 3867ed40652..c719ee638ac 100644 --- a/servers/physics_server_3d_dummy.h +++ b/servers/physics_server_3d_dummy.h @@ -357,6 +357,11 @@ public: virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) override {} virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) const override { return false; } + virtual void soft_body_apply_point_impulse(RID p_body, int p_point_index, const Vector3 &p_impulse) override {} + virtual void soft_body_apply_point_force(RID p_body, int p_point_index, const Vector3 &p_force) override {} + virtual void soft_body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override {} + virtual void soft_body_apply_central_force(RID p_body, const Vector3 &p_force) override {} + /* JOINT API */ virtual RID joint_create() override { return RID(); } diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h index 74653dac091..d9f0df971a0 100644 --- a/servers/physics_server_3d_wrap_mt.h +++ b/servers/physics_server_3d_wrap_mt.h @@ -225,6 +225,11 @@ public: FUNC3(body_apply_force, RID, const Vector3 &, const Vector3 &); FUNC2(body_apply_torque, RID, const Vector3 &); + FUNC3(soft_body_apply_point_impulse, RID, int, const Vector3 &); + FUNC3(soft_body_apply_point_force, RID, int, const Vector3 &); + FUNC2(soft_body_apply_central_impulse, RID, const Vector3 &); + FUNC2(soft_body_apply_central_force, RID, const Vector3 &); + FUNC2(body_add_constant_central_force, RID, const Vector3 &); FUNC3(body_add_constant_force, RID, const Vector3 &, const Vector3 &); FUNC2(body_add_constant_torque, RID, const Vector3 &);