diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml
index 9daac969e818..b41b37dbfd90 100644
--- a/doc/classes/CollisionObject2D.xml
+++ b/doc/classes/CollisionObject2D.xml
@@ -152,6 +152,13 @@
Returns the parent object of the given shape owner.
+
+
+
+
+ Returns the [PhysicsMaterial] (if any) set on the shape owner.
+
+
@@ -214,6 +221,14 @@
Sets the [code]one_way_collision_margin[/code] of the shape owner identified by given [param owner_id] to [param margin] pixels.
+
+
+
+
+
+ Sets the [PhysicsMaterial] for the shape owner.
+
+
diff --git a/doc/classes/CollisionObject3D.xml b/doc/classes/CollisionObject3D.xml
index d93f6a703c72..a8cc61fbc431 100644
--- a/doc/classes/CollisionObject3D.xml
+++ b/doc/classes/CollisionObject3D.xml
@@ -126,6 +126,13 @@
Returns the parent object of the given shape owner.
+
+
+
+
+ Returns the [PhysicsMaterial] (if any) set on the shape owner.
+
+
@@ -172,6 +179,14 @@
If [code]true[/code], disables the given shape owner.
+
+
+
+
+
+ Sets the [PhysicsMaterial] for the shape owner.
+
+
diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml
index e0ce05fa9c03..308cab51eb93 100644
--- a/doc/classes/CollisionShape2D.xml
+++ b/doc/classes/CollisionShape2D.xml
@@ -27,6 +27,9 @@
The margin used for one-way collision (in pixels). Higher values will make the shape thicker, and work better for colliders that enter the shape at a high velocity.
+
+ The shape's physics material. Setting a physics material lets the body have different parts with different properties, such as the head and handle of a hammer. Physics materials set on a shape take priority over those set on its body.
+
The actual shape owned by this collision shape.
diff --git a/doc/classes/CollisionShape3D.xml b/doc/classes/CollisionShape3D.xml
index 27f00be9c55c..1b07ba0205f6 100644
--- a/doc/classes/CollisionShape3D.xml
+++ b/doc/classes/CollisionShape3D.xml
@@ -39,6 +39,9 @@
A disabled collision shape has no effect in the world. This property should be changed with [method Object.set_deferred].
+
+ The shape's physics material. Setting a physics material lets the body have different parts with different properties, such as the head and handle of a hammer. Physics materials set on a shape take priority over those set on its body.
+
The actual shape owned by this collision shape.
diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml
index 75a492fb26c2..6061e44ba228 100644
--- a/doc/classes/PhysicsServer2D.xml
+++ b/doc/classes/PhysicsServer2D.xml
@@ -469,6 +469,14 @@
Returns the [RID] of the shape with the given index in the body's array of shapes.
+
+
+
+
+
+ Returns the bounciness override for the shape or [constant @GDScript.NAN] if no override is set.
+
+
@@ -476,6 +484,14 @@
Returns the number of shapes added to the body.
+
+
+
+
+
+ Returns the friction override for the shape or [constant @GDScript.NAN] if no override is set.
+
+
@@ -655,6 +671,16 @@
Sets the one-way collision properties of the body's shape with the given index. If [param enable] is [code]true[/code], the one-way collision direction given by the shape's local upward axis [code]body_get_shape_transform(body, shape_idx).y[/code] will be used to ignore collisions with the shape in the opposite direction, and to ensure depenetration of kinematic bodies happens in this direction.
+
+
+
+
+
+
+
+ Sets the bounce for the shape if [param enable] is [code]true[/code], resets to the body's value if [code]false[/code]. A negative value of [param bounce] is equivalent to enabling [member PhysicsMaterial.absorbent].
+
+
@@ -664,6 +690,16 @@
Sets the disabled property of the body's shape with the given index. If [param disabled] is [code]true[/code], then the shape will be ignored in all collision detection.
+
+
+
+
+
+
+
+ Sets the friction for the shape if [param enable] is [code]true[/code], resets to the body's value if [code]false[/code]. A negative value of [param friction] is equivalent to enabling [member PhysicsMaterial.rough].
+
+
diff --git a/doc/classes/PhysicsServer2DExtension.xml b/doc/classes/PhysicsServer2DExtension.xml
index f1a12d6da2dc..582c411ef2ea 100644
--- a/doc/classes/PhysicsServer2DExtension.xml
+++ b/doc/classes/PhysicsServer2DExtension.xml
@@ -477,6 +477,14 @@
Overridable version of [method PhysicsServer2D.body_get_shape].
+
+
+
+
+
+ Overridable version of [method PhysicsServer2D.body_get_shape_bounce_override].
+
+
@@ -484,6 +492,14 @@
Overridable version of [method PhysicsServer2D.body_get_shape_count].
+
+
+
+
+
+ Overridable version of [method PhysicsServer2D.body_get_shape_friction_override].
+
+
@@ -672,6 +688,16 @@
Overridable version of [method PhysicsServer2D.body_set_shape_as_one_way_collision].
+
+
+
+
+
+
+
+ Overridable version of [method PhysicsServer2D.body_set_shape_bounce_override].
+
+
@@ -681,6 +707,16 @@
Overridable version of [method PhysicsServer2D.body_set_shape_disabled].
+
+
+
+
+
+
+
+ Overridable version of [method PhysicsServer2D.body_set_shape_friction_override].
+
+
diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml
index 367586197838..09c4b993a4f5 100644
--- a/doc/classes/PhysicsServer3D.xml
+++ b/doc/classes/PhysicsServer3D.xml
@@ -437,6 +437,14 @@
Returns the [RID] of the nth shape of a body.
+
+
+
+
+
+ Returns the bounciness override for the shape or [constant @GDScript.NAN] if no override is set.
+
+
@@ -444,6 +452,14 @@
Returns the number of shapes assigned to a body.
+
+
+
+
+
+ Returns the friction override for the shape or [constant @GDScript.NAN] if no override is set.
+
+
@@ -644,6 +660,16 @@
Substitutes a given body shape by another. The old shape is selected by its index, the new one by its [RID].
+
+
+
+
+
+
+
+ Sets the bounciness for the shape if [param enable] is [code]true[/code], resets to the body's value if [code]false[/code]. A negative value of [param bounce] is equivalent to enabling [member PhysicsMaterial.absorbent].
+
+
@@ -652,6 +678,16 @@
+
+
+
+
+
+
+
+ Sets the friction for the shape if [param enable] is [code]true[/code], resets to the body's value if [code]false[/code]. A negative value of [param friction] is equivalent to enabling [member PhysicsMaterial.rough].
+
+
diff --git a/doc/classes/PhysicsServer3DExtension.xml b/doc/classes/PhysicsServer3DExtension.xml
index 3e283fb26c7a..7ee80477ca70 100644
--- a/doc/classes/PhysicsServer3DExtension.xml
+++ b/doc/classes/PhysicsServer3DExtension.xml
@@ -369,12 +369,28 @@
+
+
+
+
+
+ Overridable version of [method PhysicsServer3D.body_get_shape_bounce_override].
+
+
+
+
+
+
+
+ Overridable version of [method PhysicsServer3D.body_get_shape_friction_override].
+
+
@@ -556,6 +572,16 @@
+
+
+
+
+
+
+
+ Overridable version of [method PhysicsServer3D.body_set_shape_bounce_override].
+
+
@@ -564,6 +590,16 @@
+
+
+
+
+
+
+
+ Overridable version of [method PhysicsServer3D.body_set_shape_friction_override].
+
+
diff --git a/modules/godot_physics_2d/godot_body_2d.cpp b/modules/godot_physics_2d/godot_body_2d.cpp
index 65a5e1eebcf2..59ecd802e111 100644
--- a/modules/godot_physics_2d/godot_body_2d.cpp
+++ b/modules/godot_physics_2d/godot_body_2d.cpp
@@ -33,6 +33,7 @@
#include "godot_area_2d.h"
#include "godot_body_direct_state_2d.h"
#include "godot_constraint_2d.h"
+#include "godot_physics_server_2d.h"
#include "godot_space_2d.h"
void GodotBody2D::_mass_properties_changed() {
@@ -415,6 +416,35 @@ void GodotBody2D::set_space(GodotSpace2D *p_space) {
}
}
+void GodotBody2D::remove_shape(GodotShape2D *p_shape) {
+ for (int i = 0; i < get_shape_count(); i++) {
+ if (get_shape(i) == p_shape) {
+ GodotCollisionObject2D::remove_shape(i);
+ i--;
+
+ if ((int)shape_frictions.size() > i) {
+ shape_frictions.remove_at(i);
+ }
+
+ if ((int)shape_bounces.size() > i) {
+ shape_bounces.remove_at(i);
+ }
+ }
+ }
+}
+
+void GodotBody2D::remove_shape(int p_index) {
+ GodotCollisionObject2D::remove_shape(p_index);
+
+ if ((int)shape_frictions.size() > p_index) {
+ shape_frictions.remove_at(p_index);
+ }
+
+ if ((int)shape_bounces.size() > p_index) {
+ shape_bounces.remove_at(p_index);
+ }
+}
+
void GodotBody2D::_update_transform_dependent() {
center_of_mass = get_transform().basis_xform(center_of_mass_local);
}
@@ -654,6 +684,56 @@ void GodotBody2D::integrate_velocities(real_t p_step) {
_update_transform_dependent();
}
+void GodotBody2D::set_shape_friction(int p_index, real_t p_friction) {
+ ERR_FAIL_INDEX(p_index, get_shape_count());
+
+ int old_size = shape_frictions.size();
+ if (old_size <= p_index) {
+ shape_frictions.resize(p_index + 1);
+
+ for (int i = old_size; i < p_index; i++) {
+ shape_frictions[i] = NAN;
+ }
+ }
+
+ shape_frictions[p_index] = p_friction;
+}
+
+void GodotBody2D::set_shape_bounce(int p_index, real_t p_bounce) {
+ ERR_FAIL_INDEX(p_index, get_shape_count());
+
+ int old_size = shape_bounces.size();
+ if (old_size <= p_index) {
+ shape_bounces.resize(p_index + 1);
+
+ for (int i = old_size; i < p_index; i++) {
+ shape_bounces[i] = NAN;
+ }
+ }
+
+ shape_bounces[p_index] = p_bounce;
+}
+
+real_t GodotBody2D::get_shape_friction(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, get_shape_count(), NAN);
+
+ if (p_index >= (int)shape_frictions.size()) {
+ return NAN;
+ }
+
+ return shape_frictions[p_index];
+}
+
+real_t GodotBody2D::get_shape_bounce(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, get_shape_count(), NAN);
+
+ if (p_index >= (int)shape_bounces.size()) {
+ return NAN;
+ }
+
+ return shape_bounces[p_index];
+}
+
void GodotBody2D::wakeup_neighbours() {
for (const Pair &E : constraint_list) {
const GodotConstraint2D *c = E.first;
diff --git a/modules/godot_physics_2d/godot_body_2d.h b/modules/godot_physics_2d/godot_body_2d.h
index bcb55c6d3232..ff257133c66b 100644
--- a/modules/godot_physics_2d/godot_body_2d.h
+++ b/modules/godot_physics_2d/godot_body_2d.h
@@ -75,6 +75,9 @@ class GodotBody2D : public GodotCollisionObject2D {
real_t inertia = 0.0;
real_t _inv_inertia = 0.0;
+ LocalVector shape_frictions;
+ LocalVector shape_bounces;
+
Vector2 center_of_mass_local;
Vector2 center_of_mass;
@@ -305,6 +308,9 @@ class GodotBody2D : public GodotCollisionObject2D {
void set_space(GodotSpace2D *p_space) override;
+ void remove_shape(GodotShape2D *p_shape) override;
+ void remove_shape(int p_index) override;
+
void update_mass_properties();
void reset_mass_properties();
@@ -331,6 +337,12 @@ class GodotBody2D : public GodotCollisionObject2D {
return Vector2();
}
+ void set_shape_friction(int p_index, real_t p_friction);
+ void set_shape_bounce(int p_index, real_t p_bounce);
+
+ real_t get_shape_friction(int p_index) const;
+ real_t get_shape_bounce(int p_index) const;
+
void call_queries();
void wakeup_neighbours();
diff --git a/modules/godot_physics_2d/godot_body_pair_2d.cpp b/modules/godot_physics_2d/godot_body_pair_2d.cpp
index 3cb3f8ee5194..66bfbb9cf890 100644
--- a/modules/godot_physics_2d/godot_body_pair_2d.cpp
+++ b/modules/godot_physics_2d/godot_body_pair_2d.cpp
@@ -239,12 +239,32 @@ bool GodotBodyPair2D::_test_ccd(real_t p_step, GodotBody2D *p_A, int p_shape_A,
return true;
}
-real_t combine_bounce(GodotBody2D *A, GodotBody2D *B) {
- return CLAMP(A->get_bounce() + B->get_bounce(), 0, 1);
+real_t combine_bounce(GodotBody2D *p_body_a, GodotBody2D *p_body_b, int p_shape_idx_a, int p_shape_idx_b) {
+ real_t bounce_a = p_body_a->get_shape_bounce(p_shape_idx_a);
+ if (Math::is_nan(bounce_a)) {
+ bounce_a = p_body_a->get_bounce();
+ }
+
+ real_t bounce_b = p_body_b->get_shape_bounce(p_shape_idx_b);
+ if (Math::is_nan(bounce_b)) {
+ bounce_b = p_body_b->get_bounce();
+ }
+
+ return CLAMP(bounce_a + bounce_b, 0, 1);
}
-real_t combine_friction(GodotBody2D *A, GodotBody2D *B) {
- return Math::abs(MIN(A->get_friction(), B->get_friction()));
+real_t combine_friction(GodotBody2D *p_body_a, GodotBody2D *p_body_b, int p_shape_idx_a, int p_shape_idx_b) {
+ real_t friction_a = p_body_a->get_shape_friction(p_shape_idx_a);
+ if (Math::is_nan(friction_a)) {
+ friction_a = p_body_a->get_friction();
+ }
+
+ real_t friction_b = p_body_b->get_shape_friction(p_shape_idx_b);
+ if (Math::is_nan(friction_b)) {
+ friction_b = p_body_b->get_friction();
+ }
+
+ return Math::abs(MIN(friction_a, friction_b));
}
bool GodotBodyPair2D::setup(real_t p_step) {
@@ -488,7 +508,7 @@ bool GodotBodyPair2D::pre_solve(real_t p_step) {
}
#endif
- c.bounce = combine_bounce(A, B);
+ c.bounce = combine_bounce(A, B, shape_A, shape_B);
if (c.bounce) {
Vector2 crA(-A->get_prev_angular_velocity() * c.rA.y, A->get_prev_angular_velocity() * c.rA.x);
Vector2 crB(-B->get_prev_angular_velocity() * c.rB.y, B->get_prev_angular_velocity() * c.rB.x);
@@ -574,7 +594,7 @@ void GodotBodyPair2D::solve(real_t p_step) {
real_t jnOld = c.acc_normal_impulse;
c.acc_normal_impulse = MAX(jnOld + jn, 0.0f);
- real_t friction = combine_friction(A, B);
+ real_t friction = combine_friction(A, B, shape_A, shape_B);
real_t jtMax = friction * c.acc_normal_impulse;
real_t jt = -vt * c.mass_tangent;
diff --git a/modules/godot_physics_2d/godot_collision_object_2d.h b/modules/godot_physics_2d/godot_collision_object_2d.h
index e9ad969d4e95..a44107c2bb70 100644
--- a/modules/godot_physics_2d/godot_collision_object_2d.h
+++ b/modules/godot_physics_2d/godot_collision_object_2d.h
@@ -174,7 +174,7 @@ class GodotCollisionObject2D : public GodotShapeOwner2D {
_FORCE_INLINE_ real_t get_collision_priority() const { return collision_priority; }
void remove_shape(GodotShape2D *p_shape) override;
- void remove_shape(int p_index);
+ virtual void remove_shape(int p_index);
virtual void set_space(GodotSpace2D *p_space) = 0;
diff --git a/modules/godot_physics_2d/godot_physics_server_2d.cpp b/modules/godot_physics_2d/godot_physics_server_2d.cpp
index 47df6a252671..ebc94154444c 100644
--- a/modules/godot_physics_2d/godot_physics_server_2d.cpp
+++ b/modules/godot_physics_2d/godot_physics_server_2d.cpp
@@ -647,6 +647,46 @@ void GodotPhysicsServer2D::body_clear_shapes(RID p_body) {
}
}
+void GodotPhysicsServer2D::body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction) {
+ GodotBody2D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+ ERR_FAIL_INDEX(p_shape_idx, body->get_shape_count());
+
+ if (p_enable) {
+ body->set_shape_friction(p_shape_idx, p_friction);
+ } else {
+ body->set_shape_friction(p_shape_idx, NAN);
+ }
+}
+
+void GodotPhysicsServer2D::body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce) {
+ GodotBody2D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+ ERR_FAIL_INDEX(p_shape_idx, body->get_shape_count());
+
+ if (p_enable) {
+ body->set_shape_bounce(p_shape_idx, p_bounce);
+ } else {
+ body->set_shape_bounce(p_shape_idx, NAN);
+ }
+}
+
+real_t GodotPhysicsServer2D::body_get_shape_friction_override(RID p_body, int p_shape_idx) const {
+ GodotBody2D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, NAN);
+ ERR_FAIL_INDEX_V(p_shape_idx, body->get_shape_count(), NAN);
+
+ return body->get_shape_friction(p_shape_idx);
+}
+
+real_t GodotPhysicsServer2D::body_get_shape_bounce_override(RID p_body, int p_shape_idx) const {
+ GodotBody2D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, NAN);
+ ERR_FAIL_INDEX_V(p_shape_idx, body->get_shape_count(), NAN);
+
+ return body->get_shape_bounce(p_shape_idx);
+}
+
void GodotPhysicsServer2D::body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) {
GodotBody2D *body = body_owner.get_or_null(p_body);
ERR_FAIL_NULL(body);
diff --git a/modules/godot_physics_2d/godot_physics_server_2d.h b/modules/godot_physics_2d/godot_physics_server_2d.h
index 4e7eb23d1837..3f60a9bc06c3 100644
--- a/modules/godot_physics_2d/godot_physics_server_2d.h
+++ b/modules/godot_physics_2d/godot_physics_server_2d.h
@@ -184,6 +184,12 @@ class GodotPhysicsServer2D : public PhysicsServer2D {
virtual void body_remove_shape(RID p_body, int p_shape_idx) override;
virtual void body_clear_shapes(RID p_body) override;
+ virtual void body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction = 0.0) override;
+ virtual void body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce = 0.0) override;
+
+ virtual real_t body_get_shape_friction_override(RID p_body, int p_shape_idx) const override;
+ virtual real_t body_get_shape_bounce_override(RID p_body, int p_shape_idx) const override;
+
virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) override;
virtual void body_set_shape_as_one_way_collision(RID p_body, int p_shape_idx, bool p_enable, real_t p_margin) override;
diff --git a/modules/godot_physics_3d/godot_body_3d.cpp b/modules/godot_physics_3d/godot_body_3d.cpp
index 397eb0d544bd..5bdfbd1a900f 100644
--- a/modules/godot_physics_3d/godot_body_3d.cpp
+++ b/modules/godot_physics_3d/godot_body_3d.cpp
@@ -462,6 +462,35 @@ void GodotBody3D::set_space(GodotSpace3D *p_space) {
}
}
+void GodotBody3D::remove_shape(GodotShape3D *p_shape) {
+ for (int i = 0; i < get_shape_count(); i++) {
+ if (get_shape(i) == p_shape) {
+ GodotCollisionObject3D::remove_shape(i);
+ i--;
+
+ if (shape_frictions.size() > (uint32_t)i) {
+ shape_frictions.remove_at(i);
+ }
+
+ if (shape_bounces.size() > (uint32_t)i) {
+ shape_bounces.remove_at(i);
+ }
+ }
+ }
+}
+
+void GodotBody3D::remove_shape(int p_index) {
+ GodotCollisionObject3D::remove_shape(p_index);
+
+ if ((int)shape_frictions.size() > p_index) {
+ shape_frictions.remove_at(p_index);
+ }
+
+ if ((int)shape_bounces.size() > p_index) {
+ shape_bounces.remove_at(p_index);
+ }
+}
+
void GodotBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool lock) {
if (lock) {
locked_axis |= p_axis;
@@ -799,6 +828,56 @@ bool GodotBody3D::sleep_test(real_t p_step) {
}
}
+void GodotBody3D::set_shape_friction(int p_index, real_t p_friction) {
+ ERR_FAIL_INDEX(p_index, get_shape_count());
+
+ int old_size = shape_frictions.size();
+ if (old_size <= p_index) {
+ shape_frictions.resize(p_index + 1);
+
+ for (int i = old_size; i < p_index; i++) {
+ shape_frictions[i] = NAN;
+ }
+ }
+
+ shape_frictions[p_index] = p_friction;
+}
+
+void GodotBody3D::set_shape_bounce(int p_index, real_t p_bounce) {
+ ERR_FAIL_INDEX(p_index, get_shape_count());
+
+ int old_size = shape_bounces.size();
+ if (old_size <= p_index) {
+ shape_bounces.resize(p_index + 1);
+
+ for (int i = old_size; i < p_index; i++) {
+ shape_bounces[i] = NAN;
+ }
+ }
+
+ shape_bounces[p_index] = p_bounce;
+}
+
+real_t GodotBody3D::get_shape_friction(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, get_shape_count(), NAN);
+
+ if ((uint32_t)p_index >= shape_frictions.size()) {
+ return NAN;
+ }
+
+ return shape_frictions[p_index];
+}
+
+real_t GodotBody3D::get_shape_bounce(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, get_shape_count(), NAN);
+
+ if ((uint32_t)p_index >= shape_bounces.size()) {
+ return NAN;
+ }
+
+ return shape_bounces[p_index];
+}
+
void GodotBody3D::set_state_sync_callback(const Callable &p_callable) {
body_state_callback = p_callable;
}
diff --git a/modules/godot_physics_3d/godot_body_3d.h b/modules/godot_physics_3d/godot_body_3d.h
index 7dcd17b1921d..2857bdf529ea 100644
--- a/modules/godot_physics_3d/godot_body_3d.h
+++ b/modules/godot_physics_3d/godot_body_3d.h
@@ -57,6 +57,9 @@ class GodotBody3D : public GodotCollisionObject3D {
real_t friction = 1.0;
Vector3 inertia;
+ LocalVector shape_frictions;
+ LocalVector shape_bounces;
+
PhysicsServer3D::BodyDampMode linear_damp_mode = PhysicsServer3D::BODY_DAMP_MODE_COMBINE;
PhysicsServer3D::BodyDampMode angular_damp_mode = PhysicsServer3D::BODY_DAMP_MODE_COMBINE;
@@ -304,6 +307,9 @@ class GodotBody3D : public GodotCollisionObject3D {
void set_space(GodotSpace3D *p_space) override;
+ void remove_shape(GodotShape3D *p_shape) override;
+ void remove_shape(int p_index) override;
+
void update_mass_properties();
void reset_mass_properties();
@@ -337,6 +343,12 @@ class GodotBody3D : public GodotCollisionObject3D {
return p_axis.dot(_inv_inertia_tensor.xform_inv(p_axis));
}
+ void set_shape_friction(int p_index, real_t p_friction);
+ void set_shape_bounce(int p_index, real_t p_bounce);
+
+ real_t get_shape_friction(int p_index) const;
+ real_t get_shape_bounce(int p_index) const;
+
//void simulate_motion(const Transform3D& p_xform,real_t p_step);
void call_queries();
void wakeup_neighbours();
diff --git a/modules/godot_physics_3d/godot_body_pair_3d.cpp b/modules/godot_physics_3d/godot_body_pair_3d.cpp
index bc69ef1dcf6d..61dea2f6c660 100644
--- a/modules/godot_physics_3d/godot_body_pair_3d.cpp
+++ b/modules/godot_physics_3d/godot_body_pair_3d.cpp
@@ -250,12 +250,32 @@ bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A,
return true;
}
-real_t combine_bounce(GodotBody3D *A, GodotBody3D *B) {
- return CLAMP(A->get_bounce() + B->get_bounce(), 0, 1);
+real_t combine_bounce(const GodotBody3D *p_body_a, const GodotBody3D *p_body_b, int p_shape_idx_a, int p_shape_idx_b) {
+ real_t bounce_a = p_body_a->get_shape_bounce(p_shape_idx_a);
+ if (Math::is_nan(bounce_a)) {
+ bounce_a = p_body_a->get_bounce();
+ }
+
+ real_t bounce_b = p_body_b->get_shape_bounce(p_shape_idx_b);
+ if (Math::is_nan(bounce_b)) {
+ bounce_b = p_body_b->get_bounce();
+ }
+
+ return CLAMP(bounce_a + bounce_b, 0, 1);
}
-real_t combine_friction(GodotBody3D *A, GodotBody3D *B) {
- return Math::abs(MIN(A->get_friction(), B->get_friction()));
+real_t combine_friction(const GodotBody3D *p_body_a, const GodotBody3D *p_body_b, int p_shape_idx_a, int p_shape_idx_b) {
+ real_t friction_a = p_body_a->get_shape_friction(p_shape_idx_a);
+ if (Math::is_nan(friction_a)) {
+ friction_a = p_body_a->get_friction();
+ }
+
+ real_t friction_b = p_body_b->get_shape_friction(p_shape_idx_b);
+ if (Math::is_nan(friction_b)) {
+ friction_b = p_body_b->get_friction();
+ }
+
+ return Math::abs(MIN(friction_a, friction_b));
}
bool GodotBodyPair3D::setup(real_t p_step) {
@@ -439,7 +459,7 @@ bool GodotBodyPair3D::pre_solve(real_t p_step) {
B->apply_impulse(j_vec, c.rB + B->get_center_of_mass());
}
- c.bounce = combine_bounce(A, B);
+ c.bounce = combine_bounce(A, B, shape_A, shape_B);
if (c.bounce) {
Vector3 crA = A->get_prev_angular_velocity().cross(c.rA);
Vector3 crB = B->get_prev_angular_velocity().cross(c.rB);
@@ -548,7 +568,7 @@ void GodotBodyPair3D::solve(real_t p_step) {
//friction impulse
- real_t friction = combine_friction(A, B);
+ real_t friction = combine_friction(A, B, shape_A, shape_B);
Vector3 lvA = A->get_linear_velocity() + A->get_angular_velocity().cross(c.rA);
Vector3 lvB = B->get_linear_velocity() + B->get_angular_velocity().cross(c.rB);
diff --git a/modules/godot_physics_3d/godot_collision_object_3d.h b/modules/godot_physics_3d/godot_collision_object_3d.h
index 506cf06ef92b..244622f262ba 100644
--- a/modules/godot_physics_3d/godot_collision_object_3d.h
+++ b/modules/godot_physics_3d/godot_collision_object_3d.h
@@ -181,7 +181,7 @@ class GodotCollisionObject3D : public GodotShapeOwner3D {
}
void remove_shape(GodotShape3D *p_shape) override;
- void remove_shape(int p_index);
+ virtual void remove_shape(int p_index);
virtual void set_space(GodotSpace3D *p_space) = 0;
diff --git a/modules/godot_physics_3d/godot_physics_server_3d.cpp b/modules/godot_physics_3d/godot_physics_server_3d.cpp
index d5f12857d9e2..dab14370fd5e 100644
--- a/modules/godot_physics_3d/godot_physics_server_3d.cpp
+++ b/modules/godot_physics_3d/godot_physics_server_3d.cpp
@@ -134,6 +134,38 @@ real_t GodotPhysicsServer3D::shape_get_margin(RID p_shape) const {
return 0.0;
}
+real_t GodotPhysicsServer3D::body_get_shape_friction_override(RID p_body, int p_shape_idx) const {
+ const GodotBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, NAN);
+ return body->get_shape_friction(p_shape_idx);
+}
+
+void GodotPhysicsServer3D::body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction) {
+ GodotBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+ if (p_enable) {
+ body->set_shape_friction(p_shape_idx, p_friction);
+ } else {
+ body->set_shape_friction(p_shape_idx, NAN);
+ }
+}
+
+real_t GodotPhysicsServer3D::body_get_shape_bounce_override(RID p_body, int p_shape_idx) const {
+ const GodotBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, NAN);
+ return body->get_shape_bounce(p_shape_idx);
+}
+
+void GodotPhysicsServer3D::body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce) {
+ GodotBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+ if (p_enable) {
+ body->set_shape_bounce(p_shape_idx, p_bounce);
+ } else {
+ body->set_shape_bounce(p_shape_idx, NAN);
+ }
+}
+
real_t GodotPhysicsServer3D::shape_get_custom_solver_bias(RID p_shape) const {
const GodotShape3D *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_NULL_V(shape, 0);
diff --git a/modules/godot_physics_3d/godot_physics_server_3d.h b/modules/godot_physics_3d/godot_physics_server_3d.h
index 23c5d1b49ce5..34ba41b8aa60 100644
--- a/modules/godot_physics_3d/godot_physics_server_3d.h
+++ b/modules/godot_physics_3d/godot_physics_server_3d.h
@@ -98,6 +98,12 @@ class GodotPhysicsServer3D : public PhysicsServer3D {
virtual void shape_set_margin(RID p_shape, real_t p_margin) override;
virtual real_t shape_get_margin(RID p_shape) const override;
+ virtual real_t body_get_shape_friction_override(RID p_shape, int p_index) const override;
+ virtual void body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction = 0.0) override;
+
+ virtual real_t body_get_shape_bounce_override(RID p_body, int p_shape_idx) const override;
+ virtual void body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce = 0.0) override;
+
virtual real_t shape_get_custom_solver_bias(RID p_shape) const override;
/* SPACE API */
diff --git a/modules/jolt_physics/jolt_physics_server_3d.cpp b/modules/jolt_physics/jolt_physics_server_3d.cpp
index f197dec505ef..6ebbf85ceafa 100644
--- a/modules/jolt_physics/jolt_physics_server_3d.cpp
+++ b/modules/jolt_physics/jolt_physics_server_3d.cpp
@@ -173,6 +173,42 @@ real_t JoltPhysicsServer3D::shape_get_margin(RID p_shape) const {
return (real_t)shape->get_margin();
}
+real_t JoltPhysicsServer3D::body_get_shape_friction_override(RID p_body, int p_shape_idx) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, NAN);
+
+ return (real_t)body->get_shape_friction(p_shape_idx);
+}
+
+void JoltPhysicsServer3D::body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ if (p_enable) {
+ body->set_shape_friction(p_shape_idx, (float)p_friction);
+ } else {
+ body->set_shape_friction(p_shape_idx, NAN);
+ }
+}
+
+real_t JoltPhysicsServer3D::body_get_shape_bounce_override(RID p_body, int p_shape_idx) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, NAN);
+
+ return (real_t)body->get_shape_bounce(p_shape_idx);
+}
+
+void JoltPhysicsServer3D::body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ if (p_enable) {
+ body->set_shape_bounce(p_shape_idx, (float)p_bounce);
+ } else {
+ body->set_shape_bounce(p_shape_idx, NAN);
+ }
+}
+
real_t JoltPhysicsServer3D::shape_get_custom_solver_bias(RID p_shape) const {
const JoltShape3D *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_NULL_V(shape, 0.0);
diff --git a/modules/jolt_physics/jolt_physics_server_3d.h b/modules/jolt_physics/jolt_physics_server_3d.h
index 127210c7a2e0..716b61964a89 100644
--- a/modules/jolt_physics/jolt_physics_server_3d.h
+++ b/modules/jolt_physics/jolt_physics_server_3d.h
@@ -144,6 +144,12 @@ class JoltPhysicsServer3D final : public PhysicsServer3D {
virtual void shape_set_margin(RID p_shape, real_t p_margin) override;
virtual real_t shape_get_margin(RID p_shape) const override;
+ virtual real_t body_get_shape_friction_override(RID p_body, int p_shape_idx) const override;
+ virtual void body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction = 0.0) override;
+
+ virtual real_t body_get_shape_bounce_override(RID p_body, int p_shape_idx) const override;
+ virtual void body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce = 0.0) override;
+
virtual PhysicsServer3D::ShapeType shape_get_type(RID p_shape) const override;
virtual real_t shape_get_custom_solver_bias(RID p_shape) const override;
diff --git a/modules/jolt_physics/misc/jolt_math_funcs.h b/modules/jolt_physics/misc/jolt_math_funcs.h
index cf6777192f7e..296e03f9bd56 100644
--- a/modules/jolt_physics/misc/jolt_math_funcs.h
+++ b/modules/jolt_physics/misc/jolt_math_funcs.h
@@ -53,4 +53,12 @@ class JoltMath {
decompose(new_transform, r_scale);
r_new_transform = new_transform;
}
+
+ static _FORCE_INLINE_ float combine_friction(float p_friction1, float p_friction2) {
+ return Math::abs(MIN(p_friction1, p_friction2));
+ }
+
+ static _FORCE_INLINE_ float combine_bounce(float p_bounce1, float p_bounce2) {
+ return CLAMP(p_bounce1 + p_bounce2, 0.0f, 1.0f);
+ }
};
diff --git a/modules/jolt_physics/objects/jolt_body_3d.cpp b/modules/jolt_physics/objects/jolt_body_3d.cpp
index d43b6e17a6a5..451cbb57b271 100644
--- a/modules/jolt_physics/objects/jolt_body_3d.cpp
+++ b/modules/jolt_physics/objects/jolt_body_3d.cpp
@@ -133,7 +133,7 @@ void JoltBody3D::_add_to_space() {
jolt_settings->mAllowedDOFs = _calculate_allowed_dofs();
jolt_settings->mAllowDynamicOrKinematic = true;
jolt_settings->mCollideKinematicVsNonDynamic = reports_all_kinematic_contacts();
- jolt_settings->mUseManifoldReduction = !reports_contacts();
+ jolt_settings->mUseManifoldReduction = _can_use_manifold_reduction();
jolt_settings->mAllowSleeping = is_sleep_actually_allowed();
jolt_settings->mLinearDamping = 0.0f;
jolt_settings->mAngularDamping = 0.0f;
@@ -431,6 +431,34 @@ void JoltBody3D::_update_sleep_allowed() {
}
}
+void JoltBody3D::_update_manifold_reduction() {
+ const bool value = _can_use_manifold_reduction();
+
+ if (!in_space()) {
+ jolt_settings->mUseManifoldReduction = value;
+ } else {
+ space->get_body_iface().SetUseManifoldReduction(jolt_body->GetID(), value);
+ }
+}
+
+void JoltBody3D::_update_uses_shape_materials() {
+ shape_materials = false;
+
+ for (float friction : shape_frictions) {
+ if (!Math::is_nan(friction)) {
+ shape_materials = true;
+ return;
+ }
+ }
+
+ for (float bounce : shape_bounces) {
+ if (!Math::is_nan(bounce)) {
+ shape_materials = true;
+ return;
+ }
+ }
+}
+
void JoltBody3D::_destroy_joint_constraints() {
for (JoltJoint3D *joint : joints) {
joint->destroy();
@@ -475,6 +503,24 @@ void JoltBody3D::_shapes_committed() {
wake_up();
}
+void JoltBody3D::_shape_removed(int p_index) {
+ bool removed_material = false;
+
+ if ((int)shape_frictions.size() > p_index) {
+ shape_frictions.remove_at(p_index);
+ removed_material = true;
+ }
+
+ if ((int)shape_bounces.size() > p_index) {
+ shape_bounces.remove_at(p_index);
+ removed_material = true;
+ }
+
+ if (removed_material) {
+ _shape_materials_changed();
+ }
+}
+
void JoltBody3D::_space_changing() {
JoltShapedObject3D::_space_changing();
@@ -524,6 +570,7 @@ void JoltBody3D::_axis_lock_changed() {
void JoltBody3D::_contact_reporting_changed() {
_update_possible_kinematic_contacts();
_update_sleep_allowed();
+ _update_manifold_reduction();
wake_up();
}
@@ -532,6 +579,11 @@ void JoltBody3D::_sleep_allowed_changed() {
wake_up();
}
+void JoltBody3D::_shape_materials_changed() {
+ _update_uses_shape_materials();
+ _update_manifold_reduction();
+}
+
JoltBody3D::JoltBody3D() :
JoltShapedObject3D(OBJECT_TYPE_BODY),
call_queries_element(this) {
@@ -845,14 +897,6 @@ void JoltBody3D::set_max_contacts_reported(int p_count) {
contacts.resize(p_count);
contact_count = MIN(contact_count, p_count);
- const bool use_manifold_reduction = !reports_contacts();
-
- if (!in_space()) {
- jolt_settings->mUseManifoldReduction = use_manifold_reduction;
- } else {
- space->get_body_iface().SetUseManifoldReduction(jolt_body->GetID(), use_manifold_reduction);
- }
-
_contact_reporting_changed();
}
@@ -1334,3 +1378,65 @@ bool JoltBody3D::can_interact_with(const JoltSoftBody3D &p_other) const {
bool JoltBody3D::can_interact_with(const JoltArea3D &p_other) const {
return p_other.can_interact_with(*this);
}
+
+float JoltBody3D::get_shape_friction(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, (int)shapes.size(), NAN);
+
+ if (p_index >= (int)shape_frictions.size()) {
+ return NAN;
+ }
+
+ return shape_frictions[p_index];
+}
+
+void JoltBody3D::set_shape_friction(int p_index, float p_friction) {
+ ERR_FAIL_INDEX(p_index, (int)shapes.size());
+
+ int old_size = shape_frictions.size();
+ if (old_size <= p_index) {
+ shape_frictions.resize(p_index + 1);
+
+ for (int i = old_size; i < p_index; i++) {
+ shape_frictions[i] = NAN;
+ }
+ }
+
+ if (shape_frictions[p_index] == p_friction) {
+ return;
+ }
+
+ shape_frictions[p_index] = p_friction;
+
+ _shape_materials_changed();
+}
+
+float JoltBody3D::get_shape_bounce(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, (int)shapes.size(), NAN);
+
+ if (p_index >= (int)shape_bounces.size()) {
+ return NAN;
+ }
+
+ return shape_bounces[p_index];
+}
+
+void JoltBody3D::set_shape_bounce(int p_index, float p_bounce) {
+ ERR_FAIL_INDEX(p_index, (int)shapes.size());
+
+ int old_size = shape_bounces.size();
+ if (old_size <= p_index) {
+ shape_bounces.resize(p_index + 1);
+
+ for (int i = old_size; i < p_index; i++) {
+ shape_bounces[i] = NAN;
+ }
+ }
+
+ if (shape_bounces[p_index] == p_bounce) {
+ return;
+ }
+
+ shape_bounces[p_index] = p_bounce;
+
+ _shape_materials_changed();
+}
diff --git a/modules/jolt_physics/objects/jolt_body_3d.h b/modules/jolt_physics/objects/jolt_body_3d.h
index 6067b8404472..e0e09aa08964 100644
--- a/modules/jolt_physics/objects/jolt_body_3d.h
+++ b/modules/jolt_physics/objects/jolt_body_3d.h
@@ -64,6 +64,8 @@ class JoltBody3D final : public JoltShapedObject3D {
LocalVector contacts;
LocalVector areas;
LocalVector joints;
+ LocalVector shape_frictions;
+ LocalVector shape_bounces;
Variant custom_integration_userdata;
@@ -103,12 +105,15 @@ class JoltBody3D final : public JoltShapedObject3D {
bool sleep_initially = false;
bool custom_center_of_mass = false;
bool custom_integrator = false;
+ bool shape_materials = false;
virtual JPH::BroadPhaseLayer _get_broad_phase_layer() const override;
virtual JPH::ObjectLayer _get_object_layer() const override;
virtual JPH::EMotionType _get_motion_type() const override;
+ bool _can_use_manifold_reduction() const { return !reports_contacts() && !uses_shape_materials(); }
+
virtual void _add_to_space() override;
bool _should_call_queries() const { return state_sync_callback.is_valid() || custom_integration_callback.is_valid(); }
@@ -133,12 +138,15 @@ class JoltBody3D final : public JoltShapedObject3D {
void _update_joint_constraints();
void _update_possible_kinematic_contacts();
void _update_sleep_allowed();
+ void _update_manifold_reduction();
+ void _update_uses_shape_materials();
void _destroy_joint_constraints();
void _exit_all_areas();
void _mode_changed();
+ virtual void _shape_removed(int p_index) override;
virtual void _shapes_committed() override;
virtual void _space_changing() override;
virtual void _space_changed() override;
@@ -150,6 +158,7 @@ class JoltBody3D final : public JoltShapedObject3D {
void _axis_lock_changed();
void _contact_reporting_changed();
void _sleep_allowed_changed();
+ void _shape_materials_changed();
public:
JoltBody3D();
@@ -306,4 +315,12 @@ class JoltBody3D final : public JoltShapedObject3D {
virtual bool can_interact_with(const JoltBody3D &p_other) const override;
virtual bool can_interact_with(const JoltSoftBody3D &p_other) const override;
virtual bool can_interact_with(const JoltArea3D &p_other) const override;
+
+ float get_shape_friction(int p_index) const;
+ void set_shape_friction(int p_index, float p_friction);
+
+ float get_shape_bounce(int p_index) const;
+ void set_shape_bounce(int p_index, float p_bounce);
+
+ bool uses_shape_materials() const { return shape_materials; }
};
diff --git a/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp b/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
index 1e42dff8deda..2f863918b800 100644
--- a/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
+++ b/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
@@ -32,7 +32,6 @@
#include "../misc/jolt_math_funcs.h"
#include "../misc/jolt_type_conversions.h"
-#include "../shapes/jolt_custom_double_sided_shape.h"
#include "../shapes/jolt_shape_3d.h"
#include "../spaces/jolt_space_3d.h"
@@ -341,6 +340,7 @@ void JoltShapedObject3D::remove_shape(const JoltShape3D *p_shape) {
for (int i = shapes.size() - 1; i >= 0; i--) {
if (shapes[i].get_shape() == p_shape) {
shapes.remove_at(i);
+ _shape_removed(i);
}
}
@@ -350,6 +350,7 @@ void JoltShapedObject3D::remove_shape(const JoltShape3D *p_shape) {
void JoltShapedObject3D::remove_shape(int p_index) {
ERR_FAIL_INDEX(p_index, (int)shapes.size());
shapes.remove_at(p_index);
+ _shape_removed(p_index);
_shapes_changed();
}
diff --git a/modules/jolt_physics/objects/jolt_shaped_object_3d.h b/modules/jolt_physics/objects/jolt_shaped_object_3d.h
index 6cf4b7e67547..f6ef00d73df2 100644
--- a/modules/jolt_physics/objects/jolt_shaped_object_3d.h
+++ b/modules/jolt_physics/objects/jolt_shaped_object_3d.h
@@ -67,6 +67,7 @@ class JoltShapedObject3D : public JoltObject3D {
void _enqueue_needs_optimization();
void _dequeue_needs_optimization();
+ virtual void _shape_removed(int p_index) {}
virtual void _shapes_changed();
virtual void _shapes_committed();
virtual void _space_changing() override;
diff --git a/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp b/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp
index 64a4d4214c06..7b25db31c24d 100644
--- a/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp
+++ b/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp
@@ -31,6 +31,7 @@
#include "jolt_contact_listener_3d.h"
#include "../jolt_project_settings.h"
+#include "../misc/jolt_math_funcs.h"
#include "../misc/jolt_type_conversions.h"
#include "../objects/jolt_area_3d.h"
#include "../objects/jolt_body_3d.h"
@@ -39,9 +40,11 @@
#include "Jolt/Physics/Collision/EstimateCollisionResponse.h"
#include "Jolt/Physics/SoftBody/SoftBodyManifold.h"
+#include "modules/jolt_physics/shapes/jolt_shape_3d.h"
void JoltContactListener3D::OnContactAdded(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
_try_override_collision_response(p_body1, p_body2, p_settings);
+ _try_override_material(p_body1, p_body2, p_manifold, p_settings);
_try_apply_surface_velocities(p_body1, p_body2, p_settings);
_try_add_contacts(p_body1, p_body2, p_manifold, p_settings);
_try_evaluate_area_overlap(p_body1, p_body2, p_manifold);
@@ -53,6 +56,7 @@ void JoltContactListener3D::OnContactAdded(const JPH::Body &p_body1, const JPH::
void JoltContactListener3D::OnContactPersisted(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
_try_override_collision_response(p_body1, p_body2, p_settings);
+ _try_override_material(p_body1, p_body2, p_manifold, p_settings);
_try_apply_surface_velocities(p_body1, p_body2, p_settings);
_try_add_contacts(p_body1, p_body2, p_manifold, p_settings);
_try_evaluate_area_overlap(p_body1, p_body2, p_manifold);
@@ -320,6 +324,55 @@ bool JoltContactListener3D::_try_remove_area_overlap(const JPH::SubShapeIDPair &
return removed;
}
+bool JoltContactListener3D::_try_override_material(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
+ if (p_jolt_body1.IsSensor() || p_jolt_body2.IsSensor()) {
+ return false;
+ }
+
+ if (!p_jolt_body1.IsDynamic() && !p_jolt_body2.IsDynamic()) {
+ return false;
+ }
+
+ // Get friction and bounce for both bodies using per-shape materials.
+ const JoltBody3D *body1 = reinterpret_cast(p_jolt_body1.GetUserData());
+ const JoltBody3D *body2 = reinterpret_cast(p_jolt_body2.GetUserData());
+
+ if (!body1->uses_shape_materials() && !body2->uses_shape_materials()) {
+ return false;
+ }
+
+ const int shape_index1 = body1->find_shape_index(p_manifold.mSubShapeID1);
+ const int shape_index2 = body2->find_shape_index(p_manifold.mSubShapeID2);
+
+ // `find_shape_index` returns -1 when not found, treat it the same as no override.
+ float friction1 = (shape_index1 == -1) ? NAN : body1->get_shape_friction(shape_index1);
+ if (Math::is_nan(friction1)) {
+ // Per-shape friction is not set, fall back to the body's value.
+ friction1 = p_jolt_body1.GetFriction();
+ }
+
+ float bounce1 = (shape_index1 == -1) ? NAN : body1->get_shape_bounce(shape_index1);
+ if (Math::is_nan(bounce1)) {
+ // Per-shape bounce is not set, fall back to the body's value.
+ bounce1 = p_jolt_body1.GetRestitution();
+ }
+
+ float friction2 = (shape_index2 == -1) ? NAN : body2->get_shape_friction(shape_index2);
+ if (Math::is_nan(friction2)) {
+ friction2 = p_jolt_body2.GetFriction();
+ }
+
+ float bounce2 = (shape_index2 == -1) ? NAN : body2->get_shape_bounce(shape_index2);
+ if (Math::is_nan(bounce2)) {
+ bounce2 = p_jolt_body2.GetRestitution();
+ }
+
+ p_settings.mCombinedFriction = JoltMath::combine_friction(friction1, friction2);
+ p_settings.mCombinedRestitution = JoltMath::combine_bounce(bounce1, bounce2);
+
+ return true;
+}
+
#ifdef DEBUG_ENABLED
bool JoltContactListener3D::_try_add_debug_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold) {
diff --git a/modules/jolt_physics/spaces/jolt_contact_listener_3d.h b/modules/jolt_physics/spaces/jolt_contact_listener_3d.h
index ad4ce475c9d5..ca616067b389 100644
--- a/modules/jolt_physics/spaces/jolt_contact_listener_3d.h
+++ b/modules/jolt_physics/spaces/jolt_contact_listener_3d.h
@@ -112,6 +112,7 @@ class JoltContactListener3D final
bool _try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold);
bool _try_remove_contacts(const JPH::SubShapeIDPair &p_shape_pair);
bool _try_remove_area_overlap(const JPH::SubShapeIDPair &p_shape_pair);
+ bool _try_override_material(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings);
#ifdef DEBUG_ENABLED
bool _try_add_debug_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold);
diff --git a/modules/jolt_physics/spaces/jolt_space_3d.cpp b/modules/jolt_physics/spaces/jolt_space_3d.cpp
index 065d07786654..149ebfededb6 100644
--- a/modules/jolt_physics/spaces/jolt_space_3d.cpp
+++ b/modules/jolt_physics/spaces/jolt_space_3d.cpp
@@ -33,6 +33,7 @@
#include "../joints/jolt_joint_3d.h"
#include "../jolt_physics_server_3d.h"
#include "../jolt_project_settings.h"
+#include "../misc/jolt_math_funcs.h"
#include "../misc/jolt_stream_wrappers.h"
#include "../objects/jolt_area_3d.h"
#include "../objects/jolt_body_3d.h"
@@ -141,11 +142,11 @@ JoltSpace3D::JoltSpace3D(JPH::JobSystem *p_job_system) :
});
physics_system->SetCombineFriction([](const JPH::Body &p_body1, const JPH::SubShapeID &p_sub_shape_id1, const JPH::Body &p_body2, const JPH::SubShapeID &p_sub_shape_id2) {
- return Math::abs(MIN(p_body1.GetFriction(), p_body2.GetFriction()));
+ return JoltMath::combine_friction(p_body1.GetFriction(), p_body2.GetFriction());
});
physics_system->SetCombineRestitution([](const JPH::Body &p_body1, const JPH::SubShapeID &p_sub_shape_id1, const JPH::Body &p_body2, const JPH::SubShapeID &p_sub_shape_id2) {
- return CLAMP(p_body1.GetRestitution() + p_body2.GetRestitution(), 0.0f, 1.0f);
+ return JoltMath::combine_bounce(p_body1.GetRestitution(), p_body2.GetRestitution());
});
}
diff --git a/scene/2d/physics/collision_object_2d.cpp b/scene/2d/physics/collision_object_2d.cpp
index c95c84f0d9f5..2916083b9680 100644
--- a/scene/2d/physics/collision_object_2d.cpp
+++ b/scene/2d/physics/collision_object_2d.cpp
@@ -336,6 +336,38 @@ bool CollisionObject2D::is_shape_owner_disabled(uint32_t p_owner) const {
return shapes[p_owner].disabled;
}
+void CollisionObject2D::shape_owner_set_physics_material(uint32_t p_owner, const Ref &p_material) {
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ if (area) {
+ return;
+ }
+
+ ShapeData &sd = shapes[p_owner];
+ if (sd.material == p_material) {
+ return;
+ }
+ sd.material = p_material;
+
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ if (p_material.is_null()) {
+ // Disable override and use the body's physics material.
+ PhysicsServer2D::get_singleton()->body_set_shape_friction_override(rid, sd.shapes[i].index, false);
+ PhysicsServer2D::get_singleton()->body_set_shape_bounce_override(rid, sd.shapes[i].index, false);
+ } else {
+ // Enable material override for this shape.
+ PhysicsServer2D::get_singleton()->body_set_shape_friction_override(rid, sd.shapes[i].index, true, p_material->computed_friction());
+ PhysicsServer2D::get_singleton()->body_set_shape_bounce_override(rid, sd.shapes[i].index, true, p_material->computed_bounce());
+ }
+ }
+}
+
+Ref CollisionObject2D::shape_owner_get_physics_material(uint32_t p_owner) const {
+ ERR_FAIL_COND_V(!shapes.has(p_owner), nullptr);
+
+ return shapes[p_owner].material;
+}
+
void CollisionObject2D::shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable) {
if (area) {
return; //not for areas
@@ -615,6 +647,8 @@ void CollisionObject2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("shape_owner_get_owner", "owner_id"), &CollisionObject2D::shape_owner_get_owner);
ClassDB::bind_method(D_METHOD("shape_owner_set_disabled", "owner_id", "disabled"), &CollisionObject2D::shape_owner_set_disabled);
ClassDB::bind_method(D_METHOD("is_shape_owner_disabled", "owner_id"), &CollisionObject2D::is_shape_owner_disabled);
+ ClassDB::bind_method(D_METHOD("shape_owner_set_physics_material", "owner_id", "material"), &CollisionObject2D::shape_owner_set_physics_material);
+ ClassDB::bind_method(D_METHOD("shape_owner_get_physics_material", "owner_id"), &CollisionObject2D::shape_owner_get_physics_material);
ClassDB::bind_method(D_METHOD("shape_owner_set_one_way_collision", "owner_id", "enable"), &CollisionObject2D::shape_owner_set_one_way_collision);
ClassDB::bind_method(D_METHOD("is_shape_owner_one_way_collision_enabled", "owner_id"), &CollisionObject2D::is_shape_owner_one_way_collision_enabled);
ClassDB::bind_method(D_METHOD("shape_owner_set_one_way_collision_margin", "owner_id", "margin"), &CollisionObject2D::shape_owner_set_one_way_collision_margin);
diff --git a/scene/2d/physics/collision_object_2d.h b/scene/2d/physics/collision_object_2d.h
index c63d0ac40a1e..42c33d8760b7 100644
--- a/scene/2d/physics/collision_object_2d.h
+++ b/scene/2d/physics/collision_object_2d.h
@@ -33,6 +33,7 @@
#include "scene/2d/node_2d.h"
#include "scene/main/viewport.h"
#include "scene/resources/2d/shape_2d.h"
+#include "scene/resources/physics_material.h"
#include "servers/physics_server_2d.h"
class CollisionObject2D : public Node2D {
@@ -72,6 +73,8 @@ class CollisionObject2D : public Node2D {
bool disabled = false;
bool one_way_collision = false;
real_t one_way_collision_margin = 0.0;
+
+ Ref material;
};
int total_subshapes = 0;
@@ -146,6 +149,9 @@ class CollisionObject2D : public Node2D {
void shape_owner_set_disabled(uint32_t p_owner, bool p_disabled);
bool is_shape_owner_disabled(uint32_t p_owner) const;
+ void shape_owner_set_physics_material(uint32_t p_owner, const Ref &p_material);
+ Ref shape_owner_get_physics_material(uint32_t p_owner) const;
+
void shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable);
bool is_shape_owner_one_way_collision_enabled(uint32_t p_owner) const;
@@ -169,6 +175,8 @@ class CollisionObject2D : public Node2D {
_FORCE_INLINE_ RID get_rid() const { return rid; }
+ _FORCE_INLINE_ bool is_area() const { return area; }
+
CollisionObject2D();
~CollisionObject2D();
};
diff --git a/scene/2d/physics/collision_shape_2d.cpp b/scene/2d/physics/collision_shape_2d.cpp
index 47ea5bbf486a..15171aae9420 100644
--- a/scene/2d/physics/collision_shape_2d.cpp
+++ b/scene/2d/physics/collision_shape_2d.cpp
@@ -44,11 +44,18 @@ void CollisionShape2D::_update_in_shape_owner(bool p_xform_only) {
if (p_xform_only) {
return;
}
+ collision_object->shape_owner_set_physics_material(owner_id, physics_material);
collision_object->shape_owner_set_disabled(owner_id, disabled);
collision_object->shape_owner_set_one_way_collision(owner_id, one_way_collision);
collision_object->shape_owner_set_one_way_collision_margin(owner_id, one_way_collision_margin);
}
+void CollisionShape2D::_material_changed() const {
+ if (collision_object) {
+ collision_object->shape_owner_set_physics_material(owner_id, physics_material);
+ }
+}
+
void CollisionShape2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_PARENTED: {
@@ -245,6 +252,28 @@ Color CollisionShape2D::get_debug_color() const {
return debug_color;
}
+void CollisionShape2D::set_physics_material(const Ref &p_material) {
+ if (p_material == physics_material) {
+ return;
+ }
+
+ physics_material = p_material;
+
+ if (shape.is_null()) {
+ return;
+ }
+
+ _material_changed();
+
+ if (p_material.is_valid()) {
+ p_material->connect_changed(callable_mp(this, &CollisionShape2D::_material_changed));
+ }
+}
+
+Ref CollisionShape2D::get_physics_material() const {
+ return physics_material;
+}
+
#ifdef DEBUG_ENABLED
bool CollisionShape2D::_property_can_revert(const StringName &p_name) const {
@@ -283,12 +312,15 @@ void CollisionShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionShape2D::is_one_way_collision_enabled);
ClassDB::bind_method(D_METHOD("set_one_way_collision_margin", "margin"), &CollisionShape2D::set_one_way_collision_margin);
ClassDB::bind_method(D_METHOD("get_one_way_collision_margin"), &CollisionShape2D::get_one_way_collision_margin);
+ ClassDB::bind_method(D_METHOD("set_physics_material", "material"), &CollisionShape2D::set_physics_material);
+ ClassDB::bind_method(D_METHOD("get_physics_material"), &CollisionShape2D::get_physics_material);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_GROUP("One Way Collision", "one_way_collision");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_way_collision", PROPERTY_HINT_GROUP_ENABLE), "set_one_way_collision", "is_one_way_collision_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "one_way_collision_margin", PROPERTY_HINT_RANGE, "0,128,0.1,suffix:px"), "set_one_way_collision_margin", "get_one_way_collision_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material", "get_physics_material");
ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionShape2D::set_debug_color);
ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionShape2D::get_debug_color);
diff --git a/scene/2d/physics/collision_shape_2d.h b/scene/2d/physics/collision_shape_2d.h
index 5de56c80b339..2d0c6f907adb 100644
--- a/scene/2d/physics/collision_shape_2d.h
+++ b/scene/2d/physics/collision_shape_2d.h
@@ -32,6 +32,7 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/2d/shape_2d.h"
+#include "scene/resources/physics_material.h"
class CollisionObject2D;
@@ -44,8 +45,10 @@ class CollisionShape2D : public Node2D {
bool disabled = false;
bool one_way_collision = false;
real_t one_way_collision_margin = 1.0;
+ Ref physics_material;
void _shape_changed();
+ void _material_changed() const;
void _update_in_shape_owner(bool p_xform_only = false);
// Not wrapped in `#ifdef DEBUG_ENABLED` as it is used for rendering.
@@ -86,6 +89,9 @@ class CollisionShape2D : public Node2D {
void set_debug_color(const Color &p_color);
Color get_debug_color() const;
+ void set_physics_material(const Ref &p_material);
+ Ref get_physics_material() const;
+
PackedStringArray get_configuration_warnings() const override;
CollisionShape2D();
diff --git a/scene/3d/physics/collision_object_3d.cpp b/scene/3d/physics/collision_object_3d.cpp
index f181c44134e7..86f136e7d0e7 100644
--- a/scene/3d/physics/collision_object_3d.cpp
+++ b/scene/3d/physics/collision_object_3d.cpp
@@ -31,6 +31,7 @@
#include "collision_object_3d.h"
#include "scene/resources/3d/shape_3d.h"
+#include "scene/resources/physics_material.h"
void CollisionObject3D::_notification(int p_what) {
switch (p_what) {
@@ -483,6 +484,8 @@ void CollisionObject3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("shape_owner_get_owner", "owner_id"), &CollisionObject3D::shape_owner_get_owner);
ClassDB::bind_method(D_METHOD("shape_owner_set_disabled", "owner_id", "disabled"), &CollisionObject3D::shape_owner_set_disabled);
ClassDB::bind_method(D_METHOD("is_shape_owner_disabled", "owner_id"), &CollisionObject3D::is_shape_owner_disabled);
+ ClassDB::bind_method(D_METHOD("shape_owner_set_physics_material", "owner_id", "material"), &CollisionObject3D::shape_owner_set_physics_material);
+ ClassDB::bind_method(D_METHOD("shape_owner_get_physics_material", "owner_id"), &CollisionObject3D::shape_owner_get_physics_material);
ClassDB::bind_method(D_METHOD("shape_owner_add_shape", "owner_id", "shape"), &CollisionObject3D::shape_owner_add_shape);
ClassDB::bind_method(D_METHOD("shape_owner_get_shape_count", "owner_id"), &CollisionObject3D::shape_owner_get_shape_count);
ClassDB::bind_method(D_METHOD("shape_owner_get_shape", "owner_id", "shape_id"), &CollisionObject3D::shape_owner_get_shape);
@@ -565,6 +568,38 @@ bool CollisionObject3D::is_shape_owner_disabled(uint32_t p_owner) const {
return shapes[p_owner].disabled;
}
+void CollisionObject3D::shape_owner_set_physics_material(uint32_t p_owner, const Ref &p_material) {
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ if (area) {
+ return;
+ }
+
+ ShapeData &sd = shapes[p_owner];
+ if (sd.material == p_material) {
+ return;
+ }
+ sd.material = p_material;
+
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ if (p_material.is_null()) {
+ // Disable override and use the body's physics material.
+ PhysicsServer3D::get_singleton()->body_set_shape_friction_override(rid, sd.shapes[i].index, false);
+ PhysicsServer3D::get_singleton()->body_set_shape_bounce_override(rid, sd.shapes[i].index, false);
+ } else {
+ // Enable material override for this shape.
+ PhysicsServer3D::get_singleton()->body_set_shape_friction_override(rid, sd.shapes[i].index, true, p_material->computed_friction());
+ PhysicsServer3D::get_singleton()->body_set_shape_bounce_override(rid, sd.shapes[i].index, true, p_material->computed_bounce());
+ }
+ }
+}
+
+Ref CollisionObject3D::shape_owner_get_physics_material(uint32_t p_owner) const {
+ ERR_FAIL_COND_V(!shapes.has(p_owner), nullptr);
+
+ return shapes[p_owner].material;
+}
+
void CollisionObject3D::get_shape_owners(List *r_owners) {
for (const KeyValue &E : shapes) {
r_owners->push_back(E.key);
diff --git a/scene/3d/physics/collision_object_3d.h b/scene/3d/physics/collision_object_3d.h
index 8483e7700961..26a966304e12 100644
--- a/scene/3d/physics/collision_object_3d.h
+++ b/scene/3d/physics/collision_object_3d.h
@@ -32,6 +32,7 @@
#include "scene/3d/camera_3d.h"
#include "scene/3d/node_3d.h"
+#include "scene/resources/physics_material.h"
class CollisionObject3D : public Node3D {
GDCLASS(CollisionObject3D, Node3D);
@@ -68,6 +69,7 @@ class CollisionObject3D : public Node3D {
Vector shapes;
bool disabled = false;
+ Ref material;
};
int total_subshapes = 0;
@@ -154,6 +156,9 @@ class CollisionObject3D : public Node3D {
void shape_owner_set_disabled(uint32_t p_owner, bool p_disabled);
bool is_shape_owner_disabled(uint32_t p_owner) const;
+ void shape_owner_set_physics_material(uint32_t p_owner, const Ref &p_material);
+ Ref shape_owner_get_physics_material(uint32_t p_owner) const;
+
void shape_owner_add_shape(uint32_t p_owner, const Ref &p_shape);
int shape_owner_get_shape_count(uint32_t p_owner) const;
Ref shape_owner_get_shape(uint32_t p_owner, int p_shape) const;
@@ -172,6 +177,8 @@ class CollisionObject3D : public Node3D {
_FORCE_INLINE_ RID get_rid() const { return rid; }
+ _FORCE_INLINE_ bool is_area() const { return area; }
+
PackedStringArray get_configuration_warnings() const override;
CollisionObject3D();
diff --git a/scene/3d/physics/collision_shape_3d.cpp b/scene/3d/physics/collision_shape_3d.cpp
index 5031e040354c..cba00b57523b 100644
--- a/scene/3d/physics/collision_shape_3d.cpp
+++ b/scene/3d/physics/collision_shape_3d.cpp
@@ -74,6 +74,7 @@ void CollisionShape3D::_update_in_shape_owner(bool p_xform_only) {
if (p_xform_only) {
return;
}
+ collision_object->shape_owner_set_physics_material(owner_id, physics_material);
collision_object->shape_owner_set_disabled(owner_id, disabled);
}
@@ -173,6 +174,10 @@ void CollisionShape3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ClassDB::bind_method(D_METHOD("set_physics_material", "material"), &CollisionShape3D::set_physics_material);
+ ClassDB::bind_method(D_METHOD("get_physics_material"), &CollisionShape3D::get_physics_material);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material", "get_physics_material");
+
ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionShape3D::set_debug_color);
ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionShape3D::get_debug_color);
@@ -213,6 +218,7 @@ void CollisionShape3D::set_shape(const Ref &p_shape) {
shape->connect_changed(callable_mp(this, &CollisionShape3D::_shape_changed));
#endif // DEBUG_ENABLED
}
+
update_gizmos();
if (collision_object) {
collision_object->shape_owner_clear_shapes(owner_id);
@@ -232,6 +238,34 @@ Ref CollisionShape3D::get_shape() const {
return shape;
}
+void CollisionShape3D::_material_changed() const {
+ if (collision_object) {
+ collision_object->shape_owner_set_physics_material(owner_id, physics_material);
+ }
+}
+
+void CollisionShape3D::set_physics_material(const Ref &p_material) {
+ if (p_material == physics_material) {
+ return;
+ }
+
+ physics_material = p_material;
+
+ if (shape.is_null()) {
+ return;
+ }
+
+ _material_changed();
+
+ if (p_material.is_valid()) {
+ p_material->connect_changed(callable_mp(this, &CollisionShape3D::_material_changed));
+ }
+}
+
+Ref CollisionShape3D::get_physics_material() const {
+ return physics_material;
+}
+
void CollisionShape3D::set_disabled(bool p_disabled) {
disabled = p_disabled;
update_gizmos();
diff --git a/scene/3d/physics/collision_shape_3d.h b/scene/3d/physics/collision_shape_3d.h
index e0644065ffa7..bfb609774c11 100644
--- a/scene/3d/physics/collision_shape_3d.h
+++ b/scene/3d/physics/collision_shape_3d.h
@@ -32,12 +32,14 @@
#include "scene/3d/node_3d.h"
#include "scene/resources/3d/shape_3d.h"
+#include "scene/resources/physics_material.h"
class CollisionObject3D;
class CollisionShape3D : public Node3D {
GDCLASS(CollisionShape3D, Node3D);
Ref shape;
+ Ref physics_material;
uint32_t owner_id = 0;
CollisionObject3D *collision_object = nullptr;
@@ -51,6 +53,8 @@ class CollisionShape3D : public Node3D {
void _shape_changed();
#endif // DEBUG_ENABLED
+ void _material_changed() const;
+
#ifndef DISABLE_DEPRECATED
void resource_changed(Ref res);
#endif
@@ -75,6 +79,9 @@ class CollisionShape3D : public Node3D {
void set_shape(const Ref &p_shape);
Ref get_shape() const;
+ void set_physics_material(const Ref &p_material);
+ Ref get_physics_material() const;
+
void set_disabled(bool p_disabled);
bool is_disabled() const;
diff --git a/servers/extensions/physics_server_2d_extension.cpp b/servers/extensions/physics_server_2d_extension.cpp
index f8e78d655f49..23541ee07946 100644
--- a/servers/extensions/physics_server_2d_extension.cpp
+++ b/servers/extensions/physics_server_2d_extension.cpp
@@ -144,6 +144,7 @@ void PhysicsServer2DExtension::_bind_methods() {
GDVIRTUAL_BIND(_shape_get_type, "shape");
GDVIRTUAL_BIND(_shape_get_data, "shape");
GDVIRTUAL_BIND(_shape_get_custom_solver_bias, "shape");
+
GDVIRTUAL_BIND(_shape_collide, "shape_A", "xform_A", "motion_A", "shape_B", "xform_B", "motion_B", "results", "result_max", "result_count");
/* SPACE API */
@@ -180,6 +181,12 @@ void PhysicsServer2DExtension::_bind_methods() {
GDVIRTUAL_BIND(_area_remove_shape, "area", "shape_idx");
GDVIRTUAL_BIND(_area_clear_shapes, "area");
+ GDVIRTUAL_BIND(_body_set_shape_friction_override, "body", "shape_idx", "enable", "friction");
+ GDVIRTUAL_BIND(_body_set_shape_bounce_override, "body", "shape_idx", "enable", "bounce");
+
+ GDVIRTUAL_BIND(_body_get_shape_friction_override, "body", "shape_idx");
+ GDVIRTUAL_BIND(_body_get_shape_bounce_override, "body", "shape_idx");
+
GDVIRTUAL_BIND(_area_attach_object_instance_id, "area", "id");
GDVIRTUAL_BIND(_area_get_object_instance_id, "area");
diff --git a/servers/extensions/physics_server_2d_extension.h b/servers/extensions/physics_server_2d_extension.h
index 6c6d69b2dfe7..8afb98dc41b9 100644
--- a/servers/extensions/physics_server_2d_extension.h
+++ b/servers/extensions/physics_server_2d_extension.h
@@ -304,6 +304,12 @@ class PhysicsServer2DExtension : public PhysicsServer2D {
EXBIND2(body_remove_shape, RID, int)
EXBIND1(body_clear_shapes, RID)
+ EXBIND4(body_set_shape_friction_override, RID, int, bool, real_t)
+ EXBIND4(body_set_shape_bounce_override, RID, int, bool, real_t)
+
+ EXBIND2RC(real_t, body_get_shape_friction_override, RID, int)
+ EXBIND2RC(real_t, body_get_shape_bounce_override, RID, int)
+
EXBIND2(body_attach_object_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, body_get_object_instance_id, RID)
diff --git a/servers/extensions/physics_server_3d_extension.cpp b/servers/extensions/physics_server_3d_extension.cpp
index ca0423bb5eb6..5663d4f99473 100644
--- a/servers/extensions/physics_server_3d_extension.cpp
+++ b/servers/extensions/physics_server_3d_extension.cpp
@@ -232,6 +232,12 @@ void PhysicsServer3DExtension::_bind_methods() {
GDVIRTUAL_BIND(_body_remove_shape, "body", "shape_idx");
GDVIRTUAL_BIND(_body_clear_shapes, "body");
+ GDVIRTUAL_BIND(_body_set_shape_bounce_override, "body", "shape_idx", "enable", "bounce");
+ GDVIRTUAL_BIND(_body_set_shape_friction_override, "body", "shape_idx", "enable", "friction");
+
+ GDVIRTUAL_BIND(_body_get_shape_bounce_override, "body", "shape_idx");
+ GDVIRTUAL_BIND(_body_get_shape_friction_override, "body", "shape_idx");
+
GDVIRTUAL_BIND(_body_attach_object_instance_id, "body", "id");
GDVIRTUAL_BIND(_body_get_object_instance_id, "body");
diff --git a/servers/extensions/physics_server_3d_extension.h b/servers/extensions/physics_server_3d_extension.h
index acc811fbca27..0156f64060d2 100644
--- a/servers/extensions/physics_server_3d_extension.h
+++ b/servers/extensions/physics_server_3d_extension.h
@@ -305,6 +305,12 @@ class PhysicsServer3DExtension : public PhysicsServer3D {
EXBIND2(body_remove_shape, RID, int)
EXBIND1(body_clear_shapes, RID)
+ EXBIND4(body_set_shape_friction_override, RID, int, bool, real_t)
+ EXBIND4(body_set_shape_bounce_override, RID, int, bool, real_t)
+
+ EXBIND2RC(real_t, body_get_shape_friction_override, RID, int)
+ EXBIND2RC(real_t, body_get_shape_bounce_override, RID, int)
+
EXBIND2(body_attach_object_instance_id, RID, ObjectID)
EXBIND1RC(ObjectID, body_get_object_instance_id, RID)
diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp
index 10244cc05800..3741f975638b 100644
--- a/servers/physics_server_2d.cpp
+++ b/servers/physics_server_2d.cpp
@@ -695,6 +695,12 @@ void PhysicsServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("body_remove_shape", "body", "shape_idx"), &PhysicsServer2D::body_remove_shape);
ClassDB::bind_method(D_METHOD("body_clear_shapes", "body"), &PhysicsServer2D::body_clear_shapes);
+ ClassDB::bind_method(D_METHOD("body_set_shape_bounce_override", "body", "shape_idx", "enable", "bounce"), &PhysicsServer2D::body_set_shape_bounce_override, DEFVAL(0.0));
+ ClassDB::bind_method(D_METHOD("body_set_shape_friction_override", "body", "shape_idx", "enable", "friction"), &PhysicsServer2D::body_set_shape_friction_override, DEFVAL(0.0));
+
+ ClassDB::bind_method(D_METHOD("body_get_shape_bounce_override", "body", "shape_idx"), &PhysicsServer2D::body_get_shape_bounce_override);
+ ClassDB::bind_method(D_METHOD("body_get_shape_friction_override", "body", "shape_idx"), &PhysicsServer2D::body_get_shape_friction_override);
+
ClassDB::bind_method(D_METHOD("body_set_shape_disabled", "body", "shape_idx", "disabled"), &PhysicsServer2D::body_set_shape_disabled);
ClassDB::bind_method(D_METHOD("body_set_shape_as_one_way_collision", "body", "shape_idx", "enable", "margin"), &PhysicsServer2D::body_set_shape_as_one_way_collision);
diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h
index 9c5bb233e3b7..9dbc93196175 100644
--- a/servers/physics_server_2d.h
+++ b/servers/physics_server_2d.h
@@ -379,6 +379,12 @@ class PhysicsServer2D : public Object {
virtual void body_remove_shape(RID p_body, int p_shape_idx) = 0;
virtual void body_clear_shapes(RID p_body) = 0;
+ virtual void body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction = 0.0) = 0;
+ virtual void body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce = 0.0) = 0;
+
+ virtual real_t body_get_shape_friction_override(RID p_body, int p_shape_idx) const = 0;
+ virtual real_t body_get_shape_bounce_override(RID p_body, int p_shape_idx) const = 0;
+
virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) = 0;
virtual ObjectID body_get_object_instance_id(RID p_body) const = 0;
diff --git a/servers/physics_server_2d_dummy.h b/servers/physics_server_2d_dummy.h
index 71c55cf614c8..bda1303fe49b 100644
--- a/servers/physics_server_2d_dummy.h
+++ b/servers/physics_server_2d_dummy.h
@@ -226,6 +226,12 @@ class PhysicsServer2DDummy : public PhysicsServer2D {
virtual void body_remove_shape(RID p_body, int p_shape_idx) override {}
virtual void body_clear_shapes(RID p_body) override {}
+ virtual void body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction = 0.0) override {}
+ virtual void body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce = 0.0) override {}
+
+ virtual real_t body_get_shape_friction_override(RID p_body, int p_shape_idx) const override { return NAN; }
+ virtual real_t body_get_shape_bounce_override(RID p_body, int p_shape_idx) const override { return NAN; }
+
virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) override {}
virtual ObjectID body_get_object_instance_id(RID p_body) const override { return ObjectID(); }
diff --git a/servers/physics_server_2d_wrap_mt.h b/servers/physics_server_2d_wrap_mt.h
index 309bc5f43bc5..fdd2c639bf79 100644
--- a/servers/physics_server_2d_wrap_mt.h
+++ b/servers/physics_server_2d_wrap_mt.h
@@ -188,6 +188,12 @@ class PhysicsServer2DWrapMT : public PhysicsServer2D {
FUNC2(body_remove_shape, RID, int);
FUNC1(body_clear_shapes, RID);
+ FUNC4(body_set_shape_friction_override, RID, int, bool, real_t);
+ FUNC4(body_set_shape_bounce_override, RID, int, bool, real_t);
+
+ FUNC2RC(real_t, body_get_shape_friction_override, RID, int);
+ FUNC2RC(real_t, body_get_shape_bounce_override, RID, int);
+
FUNC2(body_attach_object_instance_id, RID, ObjectID);
FUNC1RC(ObjectID, body_get_object_instance_id, RID);
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index 7c907d66c329..cdd23aa086a3 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -782,6 +782,12 @@ void PhysicsServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("body_remove_shape", "body", "shape_idx"), &PhysicsServer3D::body_remove_shape);
ClassDB::bind_method(D_METHOD("body_clear_shapes", "body"), &PhysicsServer3D::body_clear_shapes);
+ ClassDB::bind_method(D_METHOD("body_set_shape_friction_override", "body", "shape_idx", "enable", "friction"), &PhysicsServer3D::body_set_shape_friction_override, DEFVAL(0.0));
+ ClassDB::bind_method(D_METHOD("body_set_shape_bounce_override", "body", "shape_idx", "enable", "bounce"), &PhysicsServer3D::body_set_shape_bounce_override, DEFVAL(0.0));
+
+ ClassDB::bind_method(D_METHOD("body_get_shape_friction_override", "body", "shape_idx"), &PhysicsServer3D::body_get_shape_friction_override);
+ ClassDB::bind_method(D_METHOD("body_get_shape_bounce_override", "body", "shape_idx"), &PhysicsServer3D::body_get_shape_bounce_override);
+
ClassDB::bind_method(D_METHOD("body_attach_object_instance_id", "body", "id"), &PhysicsServer3D::body_attach_object_instance_id);
ClassDB::bind_method(D_METHOD("body_get_object_instance_id", "body"), &PhysicsServer3D::body_get_object_instance_id);
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index 8c857f18432d..688a5d6a64ee 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -415,6 +415,12 @@ class PhysicsServer3D : public Object {
virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) = 0;
+ virtual real_t body_get_shape_friction_override(RID p_body, int p_index) const = 0;
+ virtual void body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction = 0.0) = 0;
+
+ virtual real_t body_get_shape_bounce_override(RID p_body, int p_shape_idx) const = 0;
+ virtual void body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce = 0.0) = 0;
+
virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) = 0;
virtual ObjectID body_get_object_instance_id(RID p_body) const = 0;
diff --git a/servers/physics_server_3d_dummy.h b/servers/physics_server_3d_dummy.h
index 55368c5d39a3..082a29421193 100644
--- a/servers/physics_server_3d_dummy.h
+++ b/servers/physics_server_3d_dummy.h
@@ -231,6 +231,12 @@ class PhysicsServer3DDummy : public PhysicsServer3D {
virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) override {}
+ virtual real_t body_get_shape_friction_override(RID p_shape, int p_index) const override { return NAN; }
+ virtual void body_set_shape_friction_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_friction = 0.0) override {}
+
+ virtual real_t body_get_shape_bounce_override(RID p_body, int p_shape_idx) const override { return NAN; }
+ virtual void body_set_shape_bounce_override(RID p_body, int p_shape_idx, bool p_enable, real_t p_bounce = 0.0) override {}
+
virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) override {}
virtual ObjectID body_get_object_instance_id(RID p_body) const override { return ObjectID(); }
diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h
index bd5272ff2b9e..2e5cc10abf9a 100644
--- a/servers/physics_server_3d_wrap_mt.h
+++ b/servers/physics_server_3d_wrap_mt.h
@@ -91,6 +91,12 @@ class PhysicsServer3DWrapMT : public PhysicsServer3D {
FUNC2(shape_set_margin, RID, real_t)
FUNC1RC(real_t, shape_get_margin, RID)
+ FUNC4(body_set_shape_friction_override, RID, int, bool, real_t)
+ FUNC4(body_set_shape_bounce_override, RID, int, bool, real_t)
+
+ FUNC2RC(real_t, body_get_shape_friction_override, RID, int)
+ FUNC2RC(real_t, body_get_shape_bounce_override, RID, int)
+
FUNC1RC(ShapeType, shape_get_type, RID);
FUNC1RC(Variant, shape_get_data, RID);
FUNC1RC(real_t, shape_get_custom_solver_bias, RID);