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);