Add ability for Area3D to detect/influence SoftBody3D with Jolt Physics

Co-authored-by: Jorrit Rouwe <jrouwe@gmail.com>
This commit is contained in:
Mikael Hermansson 2025-12-11 18:11:47 +01:00
parent 40448082ab
commit 1c16c4fdcc
12 changed files with 354 additions and 239 deletions

View file

@ -25,8 +25,9 @@
<method name="get_overlapping_bodies" qualifiers="const">
<return type="Node3D[]" />
<description>
Returns a list of intersecting [PhysicsBody3D]s and [GridMap]s. The overlapping body's [member CollisionObject3D.collision_layer] must be part of this area's [member CollisionObject3D.collision_mask] in order to be detected.
Returns a list of intersecting [PhysicsBody3D]s, [SoftBody3D]s, and [GridMap]s. The overlapping body's [member CollisionObject3D.collision_layer] must be part of this area's [member CollisionObject3D.collision_mask] in order to be detected.
For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
[b]Note:[/b] Godot Physics does not support reporting overlaps with [SoftBody3D], so will not return any such bodies.
</description>
</method>
<method name="has_overlapping_areas" qualifiers="const">
@ -39,8 +40,9 @@
<method name="has_overlapping_bodies" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if intersecting any [PhysicsBody3D]s or [GridMap]s, otherwise returns [code]false[/code]. The overlapping body's [member CollisionObject3D.collision_layer] must be part of this area's [member CollisionObject3D.collision_mask] in order to be detected.
Returns [code]true[/code] if intersecting any [PhysicsBody3D]s, [SoftBody3D]s, or [GridMap]s, otherwise returns [code]false[/code]. The overlapping body's [member CollisionObject3D.collision_layer] must be part of this area's [member CollisionObject3D.collision_mask] in order to be detected.
For performance reasons (collisions are all processed at the same time) the list of overlapping bodies is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
[b]Note:[/b] Godot Physics does not support reporting overlaps with [SoftBody3D], so will not consider such bodies.
</description>
</method>
<method name="overlaps_area" qualifiers="const">
@ -56,8 +58,9 @@
<param index="0" name="body" type="Node" />
<description>
Returns [code]true[/code] if the given physics body intersects or overlaps this [Area3D], [code]false[/code] otherwise.
[param body] argument can either be a [PhysicsBody3D], [SoftBody3D], or a [GridMap] instance. While GridMaps are not physics body themselves, they register their tiles with collision shapes as a virtual physics body.
[b]Note:[/b] The result of this test is not immediate after moving objects. For performance, list of overlaps is updated once per frame and before the physics step. Consider using signals instead.
The [param body] argument can either be a [PhysicsBody3D] or a [GridMap] instance. While GridMaps are not physics body themselves, they register their tiles with collision shapes as a virtual physics body.
[b]Note:[/b] Godot Physics does not support reporting overlaps with [SoftBody3D], so will return [code]false[/code] in such cases.
</description>
</method>
</methods>
@ -181,13 +184,15 @@
<signal name="body_entered">
<param index="0" name="body" type="Node3D" />
<description>
Emitted when the received [param body] enters this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
Emitted when the received [param body] enters this area. [param body] can be a [PhysicsBody3D], [SoftBody3D] or [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
[b]Note:[/b] Godot Physics does not support reporting overlaps with [SoftBody3D], so will not emit this signal in such cases.
</description>
</signal>
<signal name="body_exited">
<param index="0" name="body" type="Node3D" />
<description>
Emitted when the received [param body] exits this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
Emitted when the received [param body] exits this area. [param body] can be a [PhysicsBody3D], [SoftBody3D] or [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
[b]Note:[/b] Godot Physics does not support reporting overlaps with [SoftBody3D], so will not emit this signal in such cases.
</description>
</signal>
<signal name="body_shape_entered">
@ -196,8 +201,9 @@
<param index="2" name="body_shape_index" type="int" />
<param index="3" name="local_shape_index" type="int" />
<description>
Emitted when a [Shape3D] of the received [param body] enters a shape of this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
Emitted when a [Shape3D] of the received [param body] enters a shape of this area. [param body] can be a [PhysicsBody3D], [SoftBody3D] or [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
[param local_shape_index] and [param body_shape_index] contain indices of the interacting shapes from this area and the interacting body, respectively. [param body_rid] contains the [RID] of the body. These values can be used with the [PhysicsServer3D].
[b]Note:[/b] Godot Physics does not support reporting overlaps with [SoftBody3D], so will not emit this signal in such cases.
[b]Example:[/b] Get the [CollisionShape3D] node from the shape index:
[codeblocks]
[gdscript]
@ -216,8 +222,9 @@
<param index="2" name="body_shape_index" type="int" />
<param index="3" name="local_shape_index" type="int" />
<description>
Emitted when a [Shape3D] of the received [param body] exits a shape of this area. [param body] can be a [PhysicsBody3D] or a [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
Emitted when a [Shape3D] of the received [param body] exits a shape of this area. [param body] can be a [PhysicsBody3D], [SoftBody3D] or [GridMap]. [GridMap]s are detected if their [MeshLibrary] has collision shapes configured. Requires [member monitoring] to be set to [code]true[/code].
See also [signal body_shape_entered].
[b]Note:[/b] Godot Physics does not support reporting overlaps with [SoftBody3D], so will not emit this signal in such cases.
</description>
</signal>
</signals>

View file

@ -40,16 +40,6 @@
#include "jolt_group_filter.h"
#include "jolt_soft_body_3d.h"
namespace {
constexpr double AREA_DEFAULT_WIND_MAGNITUDE = 0.0;
constexpr double AREA_DEFAULT_WIND_ATTENUATION = 0.0;
const Vector3 AREA_DEFAULT_WIND_SOURCE = Vector3();
const Vector3 AREA_DEFAULT_WIND_DIRECTION = Vector3();
} // namespace
JPH::BroadPhaseLayer JoltArea3D::_get_broad_phase_layer() const {
return monitorable ? JoltBroadPhaseLayer::AREA_DETECTABLE : JoltBroadPhaseLayer::AREA_UNDETECTABLE;
}
@ -109,7 +99,7 @@ void JoltArea3D::_dequeue_call_queries() {
}
void JoltArea3D::_add_shape_pair(Overlap &p_overlap, const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
const JoltShapedObject3D *other_object = space->try_get_shaped(p_body_id);
const JoltObject3D *other_object = space->try_get_object(p_body_id);
ERR_FAIL_NULL(other_object);
p_overlap.rid = other_object->get_rid();
@ -117,8 +107,12 @@ void JoltArea3D::_add_shape_pair(Overlap &p_overlap, const JPH::BodyID &p_body_i
HashMap<ShapeIDPair, ShapeIndexPair, ShapeIDPair>::Iterator shape_pair = p_overlap.shape_pairs.find(ShapeIDPair(p_other_shape_id, p_self_shape_id));
if (shape_pair == p_overlap.shape_pairs.end()) {
const int other_shape_index = other_object->find_shape_index(p_other_shape_id);
const int self_shape_index = find_shape_index(p_self_shape_id);
int other_shape_index = 0;
int self_shape_index = find_shape_index(p_self_shape_id);
if (const JoltShapedObject3D *other_shaped = other_object->as_shaped()) {
other_shape_index = other_shaped->find_shape_index(p_other_shape_id);
}
shape_pair = p_overlap.shape_pairs.insert(ShapeIDPair(p_other_shape_id, p_self_shape_id), ShapeIndexPair(other_shape_index, self_shape_index));
}
@ -129,7 +123,6 @@ void JoltArea3D::_add_shape_pair(Overlap &p_overlap, const JPH::BodyID &p_body_i
bool JoltArea3D::_remove_shape_pair(Overlap &p_overlap, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
HashMap<ShapeIDPair, ShapeIndexPair, ShapeIDPair>::Iterator shape_pair = p_overlap.shape_pairs.find(ShapeIDPair(p_other_shape_id, p_self_shape_id));
if (shape_pair == p_overlap.shape_pairs.end()) {
return false;
}
@ -198,14 +191,18 @@ void JoltArea3D::_report_event(const Callable &p_callback, PhysicsServer3D::Area
}
void JoltArea3D::_notify_body_entered(const JPH::BodyID &p_body_id) {
if (JoltBody3D *other_body = space->try_get_body(p_body_id)) {
other_body->add_area(this);
if (JoltBody3D *body = space->try_get_body(p_body_id)) {
body->add_area(this);
} else if (JoltSoftBody3D *soft_body = space->try_get_soft_body(p_body_id)) {
soft_body->add_area(this);
}
}
void JoltArea3D::_notify_body_exited(const JPH::BodyID &p_body_id) {
if (JoltBody3D *other_body = space->try_get_body(p_body_id)) {
other_body->remove_area(this);
if (JoltBody3D *body = space->try_get_body(p_body_id)) {
body->remove_area(this);
} else if (JoltSoftBody3D *soft_body = space->try_get_soft_body(p_body_id)) {
soft_body->remove_area(this);
}
}
@ -234,12 +231,6 @@ void JoltArea3D::_update_group_filter() {
jolt_body->GetCollisionGroup().SetGroupFilter(JoltGroupFilter::instance);
}
void JoltArea3D::_update_default_gravity() {
if (is_default_area()) {
space->get_physics_system().SetGravity(to_jolt(gravity_vector) * gravity);
}
}
void JoltArea3D::_space_changing() {
JoltShapedObject3D::_space_changing();
@ -251,7 +242,6 @@ void JoltArea3D::_space_changed() {
JoltShapedObject3D::_space_changed();
_update_group_filter();
_update_default_gravity();
}
void JoltArea3D::_events_changed() {
@ -270,25 +260,11 @@ void JoltArea3D::_monitorable_changed() {
_update_object_layer();
}
void JoltArea3D::_gravity_changed() {
_update_default_gravity();
}
JoltArea3D::JoltArea3D() :
JoltShapedObject3D(OBJECT_TYPE_AREA),
call_queries_element(this) {
}
bool JoltArea3D::is_default_area() const {
return space != nullptr && space->get_default_area() == this;
}
void JoltArea3D::set_default_area(bool p_value) {
if (p_value) {
_update_default_gravity();
}
}
void JoltArea3D::set_transform(Transform3D p_transform) {
JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, vformat("An invalid transform was passed to area '%s'.", to_string()));
@ -342,16 +318,17 @@ Variant JoltArea3D::get_param(PhysicsServer3D::AreaParameter p_param) const {
return get_priority();
}
case PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE: {
return AREA_DEFAULT_WIND_MAGNITUDE;
// This parameter is named incorrectly. It's actually a pressure.
return get_wind_pressure();
}
case PhysicsServer3D::AREA_PARAM_WIND_SOURCE: {
return AREA_DEFAULT_WIND_SOURCE;
return get_wind_source();
}
case PhysicsServer3D::AREA_PARAM_WIND_DIRECTION: {
return AREA_DEFAULT_WIND_DIRECTION;
return get_wind_direction();
}
case PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR: {
return AREA_DEFAULT_WIND_ATTENUATION;
return get_wind_attenuation_factor();
}
default: {
ERR_FAIL_V_MSG(Variant(), vformat("Unhandled area parameter: '%d'. This should not happen. Please report this.", p_param));
@ -392,24 +369,17 @@ void JoltArea3D::set_param(PhysicsServer3D::AreaParameter p_param, const Variant
set_priority(p_value);
} break;
case PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE: {
if (!Math::is_equal_approx((double)p_value, AREA_DEFAULT_WIND_MAGNITUDE)) {
WARN_PRINT(vformat("Invalid wind force magnitude for '%s'. Area wind force magnitude is not supported when using Jolt Physics. Any such value will be ignored.", to_string()));
}
// This parameter is named incorrectly. It's actually a pressure.
set_wind_pressure(p_value);
} break;
case PhysicsServer3D::AREA_PARAM_WIND_SOURCE: {
if (!((Vector3)p_value).is_equal_approx(AREA_DEFAULT_WIND_SOURCE)) {
WARN_PRINT(vformat("Invalid wind source for '%s'. Area wind source is not supported when using Jolt Physics. Any such value will be ignored.", to_string()));
}
set_wind_source(p_value);
} break;
case PhysicsServer3D::AREA_PARAM_WIND_DIRECTION: {
if (!((Vector3)p_value).is_equal_approx(AREA_DEFAULT_WIND_DIRECTION)) {
WARN_PRINT(vformat("Invalid wind direction for '%s'. Area wind direction is not supported when using Jolt Physics. Any such value will be ignored.", to_string()));
}
set_wind_direction(p_value);
} break;
case PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR: {
if (!Math::is_equal_approx((double)p_value, AREA_DEFAULT_WIND_ATTENUATION)) {
WARN_PRINT(vformat("Invalid wind attenuation for '%s'. Area wind attenuation is not supported when using Jolt Physics. Any such value will be ignored.", to_string()));
}
set_wind_attenuation_factor(p_value);
} break;
default: {
ERR_FAIL_MSG(vformat("Unhandled area parameter: '%d'. This should not happen. Please report this.", p_param));
@ -452,7 +422,7 @@ bool JoltArea3D::can_monitor(const JoltBody3D &p_other) const {
}
bool JoltArea3D::can_monitor(const JoltSoftBody3D &p_other) const {
return false;
return is_monitoring_bodies() && (collision_mask & p_other.get_collision_layer()) != 0;
}
bool JoltArea3D::can_monitor(const JoltArea3D &p_other) const {
@ -464,67 +434,13 @@ bool JoltArea3D::can_interact_with(const JoltBody3D &p_other) const {
}
bool JoltArea3D::can_interact_with(const JoltSoftBody3D &p_other) const {
return false;
return can_monitor(p_other);
}
bool JoltArea3D::can_interact_with(const JoltArea3D &p_other) const {
return can_monitor(p_other) || p_other.can_monitor(*this);
}
Vector3 JoltArea3D::get_velocity_at_position(const Vector3 &p_position) const {
return Vector3();
}
void JoltArea3D::set_point_gravity(bool p_enabled) {
if (point_gravity == p_enabled) {
return;
}
point_gravity = p_enabled;
_gravity_changed();
}
void JoltArea3D::set_gravity(float p_gravity) {
if (gravity == p_gravity) {
return;
}
gravity = p_gravity;
_gravity_changed();
}
void JoltArea3D::set_point_gravity_distance(float p_distance) {
if (point_gravity_distance == p_distance) {
return;
}
point_gravity_distance = p_distance;
_gravity_changed();
}
void JoltArea3D::set_gravity_mode(OverrideMode p_mode) {
if (gravity_mode == p_mode) {
return;
}
gravity_mode = p_mode;
_gravity_changed();
}
void JoltArea3D::set_gravity_vector(const Vector3 &p_vector) {
if (gravity_vector == p_vector) {
return;
}
gravity_vector = p_vector;
_gravity_changed();
}
Vector3 JoltArea3D::compute_gravity(const Vector3 &p_position) const {
if (!point_gravity) {
return gravity_vector * gravity;

View file

@ -101,6 +101,8 @@ private:
OverlapsById areas_by_id;
Vector3 gravity_vector = Vector3(0, -1, 0);
Vector3 wind_source;
Vector3 wind_direction;
Callable body_monitor_callback;
Callable area_monitor_callback;
@ -110,6 +112,8 @@ private:
float point_gravity_distance = 0.0f;
float linear_damp = 0.1f;
float angular_damp = 0.1f;
float wind_pressure = 0.0f;
float wind_attenuation_factor = 0.0f;
OverrideMode gravity_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
OverrideMode linear_damp_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
@ -144,7 +148,6 @@ private:
void _update_sleeping();
void _update_group_filter();
void _update_default_gravity();
virtual void _space_changing() override;
virtual void _space_changed() override;
@ -152,14 +155,10 @@ private:
void _body_monitoring_changed();
void _area_monitoring_changed();
void _monitorable_changed();
void _gravity_changed();
public:
JoltArea3D();
bool is_default_area() const;
void set_default_area(bool p_value);
void set_transform(Transform3D p_transform);
Variant get_param(PhysicsServer3D::AreaParameter p_param) const;
@ -186,21 +185,21 @@ public:
virtual bool can_interact_with(const JoltSoftBody3D &p_other) const override;
virtual bool can_interact_with(const JoltArea3D &p_other) const override;
virtual Vector3 get_velocity_at_position(const Vector3 &p_position) const override;
virtual Vector3 get_velocity_at_position(const Vector3 &p_position) const override { return Vector3(); }
virtual bool reports_contacts() const override { return false; }
bool is_point_gravity() const { return point_gravity; }
void set_point_gravity(bool p_enabled);
void set_point_gravity(bool p_enabled) { point_gravity = p_enabled; }
float get_priority() const { return priority; }
void set_priority(float p_priority) { priority = p_priority; }
float get_gravity() const { return gravity; }
void set_gravity(float p_gravity);
void set_gravity(float p_gravity) { gravity = p_gravity; }
float get_point_gravity_distance() const { return point_gravity_distance; }
void set_point_gravity_distance(float p_distance);
void set_point_gravity_distance(float p_distance) { point_gravity_distance = p_distance; }
float get_linear_damp() const { return linear_damp; }
void set_area_linear_damp(float p_damp) { linear_damp = p_damp; }
@ -209,7 +208,7 @@ public:
void set_area_angular_damp(float p_damp) { angular_damp = p_damp; }
OverrideMode get_gravity_mode() const { return gravity_mode; }
void set_gravity_mode(OverrideMode p_mode);
void set_gravity_mode(OverrideMode p_mode) { gravity_mode = p_mode; }
OverrideMode get_linear_damp_mode() const { return linear_damp_mode; }
void set_linear_damp_mode(OverrideMode p_mode) { linear_damp_mode = p_mode; }
@ -218,7 +217,19 @@ public:
void set_angular_damp_mode(OverrideMode p_mode) { angular_damp_mode = p_mode; }
Vector3 get_gravity_vector() const { return gravity_vector; }
void set_gravity_vector(const Vector3 &p_vector);
void set_gravity_vector(const Vector3 &p_vector) { gravity_vector = p_vector; }
float get_wind_pressure() const { return wind_pressure; }
void set_wind_pressure(float p_wind_pressure) { wind_pressure = p_wind_pressure; }
float get_wind_attenuation_factor() const { return wind_attenuation_factor; }
void set_wind_attenuation_factor(float p_wind_attenuation_factor) { wind_attenuation_factor = p_wind_attenuation_factor; }
const Vector3 &get_wind_source() const { return wind_source; }
void set_wind_source(const Vector3 &p_wind_source) { wind_source = p_wind_source; }
const Vector3 &get_wind_direction() const { return wind_direction; }
void set_wind_direction(const Vector3 &p_wind_direction) { wind_direction = p_wind_direction; }
Vector3 compute_gravity(const Vector3 &p_position) const;
@ -236,4 +247,37 @@ public:
virtual bool has_custom_center_of_mass() const override { return false; }
virtual Vector3 get_center_of_mass_custom() const override { return Vector3(); }
// Incorporates the value provided by `p_getter` into `p_value` according to the override mode `p_mode`.
// Returns true if further calls to this function should stop (i.e. value has been replaced entirely).
template <typename TValue, typename TGetter>
static bool apply_override(TValue &p_value, PhysicsServer3D::AreaSpaceOverrideMode p_mode, TGetter &&p_getter);
};
template <typename TValue, typename TGetter>
inline bool JoltArea3D::apply_override(TValue &p_value, PhysicsServer3D::AreaSpaceOverrideMode p_mode, TGetter &&p_getter) {
switch (p_mode) {
case PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED: {
return false;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE: {
p_value += p_getter();
return false;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: {
p_value += p_getter();
return true;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE: {
p_value = p_getter();
return true;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: {
p_value = p_getter();
return false;
}
default: {
ERR_FAIL_V_MSG(false, vformat("Unhandled override mode: '%d'. This should not happen. Please report this.", p_mode));
}
}
}

View file

@ -42,38 +42,6 @@
#include "jolt_physics_direct_body_state_3d.h"
#include "jolt_soft_body_3d.h"
namespace {
template <typename TValue, typename TGetter>
bool integrate(TValue &p_value, PhysicsServer3D::AreaSpaceOverrideMode p_mode, TGetter &&p_getter) {
switch (p_mode) {
case PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED: {
return false;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE: {
p_value += p_getter();
return false;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: {
p_value += p_getter();
return true;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE: {
p_value = p_getter();
return true;
}
case PhysicsServer3D::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: {
p_value = p_getter();
return false;
}
default: {
ERR_FAIL_V_MSG(false, vformat("Unhandled override mode: '%d'. This should not happen. Please report this.", p_mode));
}
}
}
} // namespace
JPH::BroadPhaseLayer JoltBody3D::_get_broad_phase_layer() const {
switch (mode) {
case PhysicsServer3D::BODY_MODE_STATIC: {
@ -319,7 +287,7 @@ void JoltBody3D::_update_gravity(JPH::Body &p_jolt_body) {
bool gravity_done = false;
for (const JoltArea3D *area : areas) {
gravity_done = integrate(gravity, area->get_gravity_mode(), [&]() {
gravity_done = JoltArea3D::apply_override(gravity, area->get_gravity_mode(), [&]() {
return area->compute_gravity(position);
});
@ -332,7 +300,7 @@ void JoltBody3D::_update_gravity(JPH::Body &p_jolt_body) {
gravity += space->get_default_area()->compute_gravity(position);
}
gravity *= gravity_scale;
gravity *= p_jolt_body.GetMotionPropertiesUnchecked()->GetGravityFactor();
}
void JoltBody3D::_update_damp() {
@ -348,13 +316,13 @@ void JoltBody3D::_update_damp() {
for (const JoltArea3D *area : areas) {
if (!linear_damp_done) {
linear_damp_done = integrate(total_linear_damp, area->get_linear_damp_mode(), [&]() {
linear_damp_done = JoltArea3D::apply_override(total_linear_damp, area->get_linear_damp_mode(), [&]() {
return area->get_linear_damp();
});
}
if (!angular_damp_done) {
angular_damp_done = integrate(total_angular_damp, area->get_angular_damp_mode(), [&]() {
angular_damp_done = JoltArea3D::apply_override(total_angular_damp, area->get_angular_damp_mode(), [&]() {
return area->get_angular_damp();
});
}
@ -1247,14 +1215,20 @@ void JoltBody3D::set_friction(float p_friction) {
}
}
void JoltBody3D::set_gravity_scale(float p_scale) {
if (gravity_scale == p_scale) {
return;
float JoltBody3D::get_gravity_scale() const {
if (!in_space()) {
return jolt_settings->mGravityFactor;
} else {
return jolt_body->GetMotionPropertiesUnchecked()->GetGravityFactor();
}
}
gravity_scale = p_scale;
_motion_changed();
void JoltBody3D::set_gravity_scale(float p_scale) {
if (!in_space()) {
jolt_settings->mGravityFactor = p_scale;
} else {
jolt_body->GetMotionPropertiesUnchecked()->SetGravityFactor(p_scale);
}
}
void JoltBody3D::set_linear_damp(float p_damp) {

View file

@ -92,7 +92,6 @@ private:
float angular_damp = 0.0f;
float total_linear_damp = 0.0f;
float total_angular_damp = 0.0f;
float gravity_scale = 1.0f;
float collision_priority = 1.0f;
int contact_count = 0;
@ -276,7 +275,7 @@ public:
float get_friction() const;
void set_friction(float p_friction);
float get_gravity_scale() const { return gravity_scale; }
float get_gravity_scale() const;
void set_gravity_scale(float p_scale);
Vector3 get_gravity() const { return gravity; }

View file

@ -195,7 +195,6 @@ JoltShapedObject3D::JoltShapedObject3D(ObjectType p_object_type) :
jolt_settings->mRestitution = 0.0f;
jolt_settings->mLinearDamping = 0.0f;
jolt_settings->mAngularDamping = 0.0f;
jolt_settings->mGravityFactor = 0.0f;
}
JoltShapedObject3D::~JoltShapedObject3D() {

View file

@ -228,6 +228,87 @@ JPH::SoftBodySharedSettings *JoltSoftBody3D::_create_shared_settings() {
return settings;
}
void JoltSoftBody3D::_apply_environmental_forces(float p_step, JPH::Body &p_jolt_body) {
// Get approximation of the center of the soft body.
Vector3 com_position = to_godot(p_jolt_body.GetCenterOfMassPosition());
// Calculate gravity and which areas affect the soft body through wind.
bool gravity_done = false;
Vector3 gravity;
LocalVector<JoltArea3D *> wind_areas;
for (JoltArea3D *area : areas) {
if (!gravity_done) {
gravity_done = JoltArea3D::apply_override(gravity, area->get_gravity_mode(), [&]() {
return area->compute_gravity(com_position);
});
}
if (area->get_wind_pressure() > CMP_EPSILON) {
wind_areas.push_back(area);
}
}
// Add default gravity.
if (!gravity_done) {
gravity += space->get_default_area()->compute_gravity(com_position);
}
// Apply gravity to soft body. Note that this only works so long as vertices have uniform mass (excluding pinned vertices).
p_jolt_body.AddForce(to_jolt(gravity) * mass);
if (!wind_areas.is_empty()) {
JPH::SoftBodyMotionProperties &motion_properties = static_cast<JPH::SoftBodyMotionProperties &>(*p_jolt_body.GetMotionPropertiesUnchecked());
JPH::Array<JPH::SoftBodyVertex> &physics_vertices = motion_properties.GetVertices();
for (const JPH::SoftBodySharedSettings::Face &physics_face : motion_properties.GetFaces()) {
JPH::SoftBodyVertex &physics_vertex0 = physics_vertices[physics_face.mVertex[0]];
JPH::SoftBodyVertex &physics_vertex1 = physics_vertices[physics_face.mVertex[1]];
JPH::SoftBodyVertex &physics_vertex2 = physics_vertices[physics_face.mVertex[2]];
// Calculate the triangle centroid.
Vector3 v0 = to_godot(physics_vertex0.mPosition);
Vector3 v1 = to_godot(physics_vertex1.mPosition);
Vector3 v2 = to_godot(physics_vertex2.mPosition);
Vector3 centroid = com_position + (v0 + v1 + v2) * real_t(1.0 / 3.0);
// Calculate the triangle normal.
Vector3 normal = (v2 - v0).cross(v1 - v0);
real_t normal_length = normal.length();
if (normal_length > real_t(1.0e-6)) { // If the normal is near zero, the area is near zero so we can skip this triangle.
normal /= normal_length;
// Area is half the length of the cross product of two sides.
real_t triangle_area = real_t(0.5) * normal_length;
// Accumulate wind forces from all wind areas.
Vector3 wind_force;
for (const JoltArea3D *area : wind_areas) {
const Vector3 &wind_direction = area->get_wind_direction();
const Vector3 &wind_source = area->get_wind_source();
// Calculate attenuation factor based on distance from wind source to triangle centroid.
// We do not allow a projection below 1 to ensure that we never amplify and to avoid NaNs when the value would be negative.
real_t projection_toward_centroid = MAX((centroid - wind_source).dot(wind_direction), real_t(1.0));
real_t attenuation_over_distance = Math::pow(projection_toward_centroid, -real_t(area->get_wind_attenuation_factor()));
// Calculate force magnitude.
real_t force_magnitude = area->get_wind_pressure() * triangle_area;
// Calculate the resulting wind force on the triangle by projecting wind direction onto triangle normal.
// Divide by 3 to distribute force equally over each vertex.
wind_force += (force_magnitude * attenuation_over_distance * real_t(1.0 / 3.0) * normal.dot(wind_direction)) * normal;
}
// Apply the force as an impulse over the timestep.
JPH::Vec3 impulse = to_jolt(wind_force * p_step);
physics_vertex0.mVelocity += impulse * physics_vertex0.mInvMass;
physics_vertex1.mVelocity += impulse * physics_vertex1.mInvMass;
physics_vertex2.mVelocity += impulse * physics_vertex2.mInvMass;
}
}
}
}
void JoltSoftBody3D::_update_mass() {
if (!in_space()) {
return;
@ -331,6 +412,14 @@ void JoltSoftBody3D::_motion_changed() {
wake_up();
}
void JoltSoftBody3D::_transform_changed() {
wake_up();
}
void JoltSoftBody3D::_areas_changed() {
wake_up();
}
JoltSoftBody3D::JoltSoftBody3D() :
JoltObject3D(OBJECT_TYPE_SOFT_BODY) {
jolt_settings->mRestitution = 0.0f;
@ -362,6 +451,25 @@ bool JoltSoftBody3D::has_collision_exception(const RID &p_excepted_body) const {
return exceptions.find(p_excepted_body) >= 0;
}
void JoltSoftBody3D::add_area(JoltArea3D *p_area) {
int i = 0;
for (; i < (int)areas.size(); i++) {
if (p_area->get_priority() > areas[i]->get_priority()) {
break;
}
}
areas.insert(i, p_area);
_areas_changed();
}
void JoltSoftBody3D::remove_area(JoltArea3D *p_area) {
areas.erase(p_area);
_areas_changed();
}
bool JoltSoftBody3D::can_interact_with(const JoltBody3D &p_other) const {
return (can_collide_with(p_other) || p_other.can_collide_with(*this)) && !has_collision_exception(p_other.get_rid()) && !p_other.has_collision_exception(rid);
}
@ -378,6 +486,10 @@ Vector3 JoltSoftBody3D::get_velocity_at_position(const Vector3 &p_position) cons
return Vector3();
}
void JoltSoftBody3D::pre_step(float p_step, JPH::Body &p_jolt_body) {
_apply_environmental_forces(p_step, p_jolt_body);
}
void JoltSoftBody3D::set_mesh(const RID &p_mesh) {
if (unlikely(mesh == p_mesh)) {
return;
@ -608,7 +720,8 @@ void JoltSoftBody3D::set_transform(const Transform3D &p_transform) {
vertex.mPosition = vertex.mPreviousPosition = relative_transform.Multiply3x3(vertex.mPosition);
vertex.mVelocity = JPH::Vec3::sZero();
}
wake_up();
_transform_changed();
}
AABB JoltSoftBody3D::get_bounds() const {

View file

@ -39,15 +39,18 @@
#include "Jolt/Physics/SoftBody/SoftBodyCreationSettings.h"
#include "Jolt/Physics/SoftBody/SoftBodySharedSettings.h"
class JoltArea3D;
class JoltSpace3D;
class JoltSoftBody3D final : public JoltObject3D {
HashSet<int> pinned_vertices;
LocalVector<RID> exceptions;
LocalVector<int> mesh_to_physics;
LocalVector<JoltArea3D *> areas;
LocalVector<Vector3> normals;
LocalVector<RID> exceptions;
RID mesh;
LocalVector<int> mesh_to_physics;
JPH::SoftBodyCreationSettings *jolt_settings = new JPH::SoftBodyCreationSettings();
@ -69,6 +72,8 @@ class JoltSoftBody3D final : public JoltObject3D {
JPH::SoftBodySharedSettings *_create_shared_settings();
void _apply_environmental_forces(float p_step, JPH::Body &p_jolt_body);
void _update_mass();
void _update_pressure();
void _update_damping();
@ -86,6 +91,8 @@ class JoltSoftBody3D final : public JoltObject3D {
void _vertices_changed();
void _exceptions_changed();
void _motion_changed();
void _transform_changed();
void _areas_changed();
public:
JoltSoftBody3D();
@ -97,6 +104,9 @@ public:
const LocalVector<RID> &get_collision_exceptions() const { return exceptions; }
void add_area(JoltArea3D *p_area);
void remove_area(JoltArea3D *p_area);
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;
@ -105,6 +115,8 @@ public:
virtual Vector3 get_velocity_at_position(const Vector3 &p_position) const override;
virtual void pre_step(float p_step, JPH::Body &p_jolt_body) override;
void set_mesh(const RID &p_mesh);
bool is_pickable() const { return pickable; }

View file

@ -44,7 +44,7 @@ void JoltContactListener3D::OnContactAdded(const JPH::Body &p_body1, const JPH::
_try_override_collision_response(p_body1, p_body2, 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);
_try_evaluate_area_overlap(p_body1, p_body2, p_manifold.mSubShapeID1, p_manifold.mSubShapeID2);
#ifdef DEBUG_ENABLED
_try_add_debug_contacts(p_body1, p_body2, p_manifold);
@ -55,7 +55,7 @@ void JoltContactListener3D::OnContactPersisted(const JPH::Body &p_body1, const J
_try_override_collision_response(p_body1, p_body2, 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);
_try_evaluate_area_overlap(p_body1, p_body2, p_manifold.mSubShapeID1, p_manifold.mSubShapeID2);
#ifdef DEBUG_ENABLED
_try_add_debug_contacts(p_body1, p_body2, p_manifold);
@ -74,17 +74,20 @@ void JoltContactListener3D::OnContactRemoved(const JPH::SubShapeIDPair &p_shape_
JPH::SoftBodyValidateResult JoltContactListener3D::OnSoftBodyContactValidate(const JPH::Body &p_soft_body, const JPH::Body &p_other_body, JPH::SoftBodyContactSettings &p_settings) {
_try_override_collision_response(p_soft_body, p_other_body, p_settings);
return JPH::SoftBodyValidateResult::AcceptContact;
}
#ifdef DEBUG_ENABLED
void JoltContactListener3D::OnSoftBodyContactAdded(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold) {
_try_add_debug_contacts(p_soft_body, p_manifold);
}
for (JPH::uint i = 0; i < p_manifold.GetNumSensorContacts(); i++) {
if (JPH::Body *other_jolt_body = space->try_get_jolt_body(p_manifold.GetSensorContactBodyID(i))) {
_try_evaluate_area_overlap(p_soft_body, *other_jolt_body, JPH::SubShapeID(), JPH::SubShapeID());
}
}
#ifdef DEBUG_ENABLED
_try_add_debug_contacts(p_soft_body, p_manifold);
#endif
}
bool JoltContactListener3D::_try_override_collision_response(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, JPH::ContactSettings &p_settings) {
if (p_jolt_body1.IsSensor() || p_jolt_body2.IsSensor()) {
@ -247,37 +250,13 @@ bool JoltContactListener3D::_try_add_contacts(const JPH::Body &p_jolt_body1, con
return true;
}
bool JoltContactListener3D::_try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold) {
bool JoltContactListener3D::_try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::SubShapeID &p_shape_id1, const JPH::SubShapeID &p_shape_id2) {
if (!p_body1.IsSensor() && !p_body2.IsSensor()) {
return false;
}
auto has_shifted = [](const JoltShapedObject3D &p_object, const JPH::SubShapeID &p_sub_shape_id) {
return p_object.get_previous_jolt_shape() != nullptr && p_object.get_jolt_shape()->GetSubShapeUserData(p_sub_shape_id) != p_object.get_previous_jolt_shape()->GetSubShapeUserData(p_sub_shape_id);
};
auto evaluate = [&](const JoltArea3D &p_area, const auto &p_object, const JPH::SubShapeIDPair &p_shape_pair) {
const MutexLock write_lock(write_mutex);
if (p_area.can_monitor(p_object)) {
if (!area_overlaps.has(p_shape_pair)) {
area_overlaps.insert(p_shape_pair);
area_enters.insert(p_shape_pair);
} else if (has_shifted(p_area, p_shape_pair.GetSubShapeID1()) || has_shifted(p_object, p_shape_pair.GetSubShapeID2())) {
// A shape has taken on the `JPH::SubShapeID` value of another shape, likely because of the other shape having been replaced or moved
// in some way, so we force the area to refresh its internal mappings by exiting and entering this shape pair.
area_exits.insert(p_shape_pair);
area_enters.insert(p_shape_pair);
}
} else {
if (area_overlaps.erase(p_shape_pair)) {
area_exits.insert(p_shape_pair);
}
}
};
const JPH::SubShapeIDPair shape_pair1(p_body1.GetID(), p_manifold.mSubShapeID1, p_body2.GetID(), p_manifold.mSubShapeID2);
const JPH::SubShapeIDPair shape_pair2(p_body2.GetID(), p_manifold.mSubShapeID2, p_body1.GetID(), p_manifold.mSubShapeID1);
const JPH::SubShapeIDPair shape_pair1(p_body1.GetID(), p_shape_id1, p_body2.GetID(), p_shape_id2);
const JPH::SubShapeIDPair shape_pair2(p_body2.GetID(), p_shape_id2, p_body1.GetID(), p_shape_id1);
const JoltObject3D *object1 = reinterpret_cast<JoltObject3D *>(p_body1.GetUserData());
const JoltObject3D *object2 = reinterpret_cast<JoltObject3D *>(p_body2.GetUserData());
@ -288,13 +267,20 @@ bool JoltContactListener3D::_try_evaluate_area_overlap(const JPH::Body &p_body1,
const JoltBody3D *body1 = object1->as_body();
const JoltBody3D *body2 = object2->as_body();
const JoltSoftBody3D *soft_body1 = object1->as_soft_body();
const JoltSoftBody3D *soft_body2 = object2->as_soft_body();
if (area1 != nullptr && area2 != nullptr) {
evaluate(*area1, *area2, shape_pair1);
evaluate(*area2, *area1, shape_pair2);
_evaluate_area_overlap(*area1, *area2, shape_pair1);
_evaluate_area_overlap(*area2, *area1, shape_pair2);
} else if (area1 != nullptr && body2 != nullptr) {
evaluate(*area1, *body2, shape_pair1);
_evaluate_area_overlap(*area1, *body2, shape_pair1);
} else if (area2 != nullptr && body1 != nullptr) {
evaluate(*area2, *body1, shape_pair2);
_evaluate_area_overlap(*area2, *body1, shape_pair2);
} else if (area1 != nullptr && soft_body2 != nullptr) {
_evaluate_area_overlap(*area1, *soft_body2, shape_pair1);
} else if (area2 != nullptr && soft_body1 != nullptr) {
_evaluate_area_overlap(*area2, *soft_body1, shape_pair2);
}
return true;
@ -302,7 +288,6 @@ bool JoltContactListener3D::_try_evaluate_area_overlap(const JPH::Body &p_body1,
bool JoltContactListener3D::_try_remove_contacts(const JPH::SubShapeIDPair &p_shape_pair) {
const MutexLock write_lock(write_mutex);
return manifolds_by_shape_pair.erase(p_shape_pair);
}
@ -414,6 +399,61 @@ bool JoltContactListener3D::_try_add_debug_contacts(const JPH::Body &p_soft_body
#endif
bool JoltContactListener3D::_has_shape_shifted(const JoltShapedObject3D &p_object, const JPH::SubShapeID &p_sub_shape_id) {
return p_object.get_previous_jolt_shape() != nullptr && p_object.get_jolt_shape()->GetSubShapeUserData(p_sub_shape_id) != p_object.get_previous_jolt_shape()->GetSubShapeUserData(p_sub_shape_id);
}
void JoltContactListener3D::_evaluate_area_overlap(const JoltArea3D &p_area, const JoltArea3D &p_other_area, const JPH::SubShapeIDPair &p_shape_pair) {
const MutexLock write_lock(write_mutex);
if (p_area.can_monitor(p_other_area)) {
if (!area_overlaps.has(p_shape_pair)) {
area_overlaps.insert(p_shape_pair);
area_enters.insert(p_shape_pair);
} else if (_has_shape_shifted(p_area, p_shape_pair.GetSubShapeID1()) || _has_shape_shifted(p_other_area, p_shape_pair.GetSubShapeID2())) {
// A shape has taken on the `JPH::SubShapeID` value of another shape, likely because of the other shape having been replaced or moved
// in some way, so we force the area to refresh its internal mappings by exiting and entering this shape pair.
area_exits.insert(p_shape_pair);
area_enters.insert(p_shape_pair);
}
} else {
if (area_overlaps.erase(p_shape_pair)) {
area_exits.insert(p_shape_pair);
}
}
}
void JoltContactListener3D::_evaluate_area_overlap(const JoltArea3D &p_area, const JoltBody3D &p_body, const JPH::SubShapeIDPair &p_shape_pair) {
const MutexLock write_lock(write_mutex);
if (p_area.can_monitor(p_body)) {
if (!area_overlaps.has(p_shape_pair)) {
area_overlaps.insert(p_shape_pair);
area_enters.insert(p_shape_pair);
} else if (_has_shape_shifted(p_area, p_shape_pair.GetSubShapeID1()) || _has_shape_shifted(p_body, p_shape_pair.GetSubShapeID2())) {
// A shape has taken on the `JPH::SubShapeID` value of another shape, likely because of the other shape having been replaced or moved
// in some way, so we force the area to refresh its internal mappings by exiting and entering this shape pair.
area_exits.insert(p_shape_pair);
area_enters.insert(p_shape_pair);
}
} else {
if (area_overlaps.erase(p_shape_pair)) {
area_exits.insert(p_shape_pair);
}
}
}
void JoltContactListener3D::_evaluate_area_overlap(const JoltArea3D &p_area, const JoltSoftBody3D &p_body, const JPH::SubShapeIDPair &p_shape_pair) {
const MutexLock write_lock(write_mutex);
if (p_area.can_monitor(p_body)) {
area_soft_body_overlaps.insert(p_shape_pair);
if (!area_exits.erase(p_shape_pair)) {
area_enters.insert(p_shape_pair);
}
}
}
void JoltContactListener3D::_flush_contacts() {
for (KeyValue<JPH::SubShapeIDPair, Manifold> &E : manifolds_by_shape_pair) {
const JPH::SubShapeIDPair &shape_pair = E.key;
@ -504,7 +544,18 @@ void JoltContactListener3D::_flush_area_exits() {
area_exits.clear();
}
void JoltContactListener3D::_clear_area_soft_body_overlaps() {
area_exits.reserve(area_soft_body_overlaps.size());
for (const JPH::SubShapeIDPair &shape_pair : area_soft_body_overlaps) {
area_exits.insert(shape_pair);
}
area_soft_body_overlaps.clear();
}
void JoltContactListener3D::pre_step() {
_clear_area_soft_body_overlaps();
#ifdef DEBUG_ENABLED
debug_contact_count = 0;
#endif

View file

@ -46,7 +46,10 @@
#include <new>
class JoltArea3D;
class JoltBody3D;
class JoltShapedObject3D;
class JoltSoftBody3D;
class JoltSpace3D;
class JoltContactListener3D final
@ -85,6 +88,7 @@ class JoltContactListener3D final
HashMap<JPH::SubShapeIDPair, Manifold, ShapePairHasher> manifolds_by_shape_pair;
HashSet<JPH::SubShapeIDPair, ShapePairHasher> area_overlaps;
HashSet<JPH::SubShapeIDPair, ShapePairHasher> area_soft_body_overlaps;
HashSet<JPH::SubShapeIDPair, ShapePairHasher> area_enters;
HashSet<JPH::SubShapeIDPair, ShapePairHasher> area_exits;
Mutex write_mutex;
@ -100,16 +104,13 @@ class JoltContactListener3D final
virtual void OnContactRemoved(const JPH::SubShapeIDPair &p_shape_pair) override;
virtual JPH::SoftBodyValidateResult OnSoftBodyContactValidate(const JPH::Body &p_soft_body, const JPH::Body &p_other_body, JPH::SoftBodyContactSettings &p_settings) override;
#ifdef DEBUG_ENABLED
virtual void OnSoftBodyContactAdded(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold) override;
#endif
bool _try_override_collision_response(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, JPH::ContactSettings &p_settings);
bool _try_override_collision_response(const JPH::Body &p_jolt_soft_body, const JPH::Body &p_jolt_other_body, JPH::SoftBodyContactSettings &p_settings);
bool _try_apply_surface_velocities(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, JPH::ContactSettings &p_settings);
bool _try_add_contacts(const JPH::Body &p_jolt_body1, const JPH::Body &p_jolt_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings);
bool _try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold);
bool _try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::SubShapeID &p_shape_id1, const JPH::SubShapeID &p_shape_id2);
bool _try_remove_contacts(const JPH::SubShapeIDPair &p_shape_pair);
bool _try_remove_area_overlap(const JPH::SubShapeIDPair &p_shape_pair);
@ -118,9 +119,16 @@ class JoltContactListener3D final
bool _try_add_debug_contacts(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold);
#endif
bool _has_shape_shifted(const JoltShapedObject3D &p_object, const JPH::SubShapeID &p_sub_shape_id);
void _evaluate_area_overlap(const JoltArea3D &p_area, const JoltArea3D &p_other_area, const JPH::SubShapeIDPair &p_shape_pair);
void _evaluate_area_overlap(const JoltArea3D &p_area, const JoltBody3D &p_body, const JPH::SubShapeIDPair &p_shape_pair);
void _evaluate_area_overlap(const JoltArea3D &p_area, const JoltSoftBody3D &p_body, const JPH::SubShapeIDPair &p_shape_pair);
void _flush_contacts();
void _flush_area_enters();
void _flush_area_exits();
void _clear_area_soft_body_overlaps();
public:
explicit JoltContactListener3D(JoltSpace3D *p_space) :

View file

@ -85,6 +85,15 @@ void JoltSpace3D::_pre_step(float p_step) {
JoltObject3D *object = reinterpret_cast<JoltObject3D *>(jolt_body->GetUserData());
object->pre_step(p_step, *jolt_body);
}
const JPH::BodyID *active_soft_bodies = physics_system->GetActiveBodiesUnsafe(JPH::EBodyType::SoftBody);
const JPH::uint32 active_soft_body_count = physics_system->GetNumActiveBodies(JPH::EBodyType::SoftBody);
for (JPH::uint32 i = 0; i < active_soft_body_count; i++) {
JPH::Body *jolt_body = lock_iface.TryGetBody(active_soft_bodies[i]);
JoltObject3D *object = reinterpret_cast<JoltObject3D *>(jolt_body->GetUserData());
object->pre_step(p_step, *jolt_body);
}
}
void JoltSpace3D::_post_step(float p_step) {
@ -382,22 +391,6 @@ JoltPhysicsDirectSpaceState3D *JoltSpace3D::get_direct_state() {
return direct_state;
}
void JoltSpace3D::set_default_area(JoltArea3D *p_area) {
if (default_area == p_area) {
return;
}
if (default_area != nullptr) {
default_area->set_default_area(false);
}
default_area = p_area;
if (default_area != nullptr) {
default_area->set_default_area(true);
}
}
JPH::Body *JoltSpace3D::add_object(const JoltObject3D &p_object, const JPH::BodyCreationSettings &p_settings, bool p_sleeping) {
JPH::BodyInterface &body_iface = get_body_iface();
JPH::Body *jolt_body = body_iface.CreateBody(p_settings);
@ -583,8 +576,7 @@ void JoltSpace3D::dump_debug_snapshot(const String &p_dir) {
const JoltObject3D *object = reinterpret_cast<const JoltObject3D *>(settings.mUserData);
if (const JoltBody3D *body = object->as_body()) {
// Since we do our own integration of gravity and damping, while leaving Jolt's own values at zero, we need to transfer over the correct values.
settings.mGravityFactor = body->get_gravity_scale();
// Since we apply our own damping, while leaving Jolt's own values at zero, we need to transfer over the correct values.
settings.mLinearDamping = body->get_total_linear_damp();
settings.mAngularDamping = body->get_total_angular_damp();
}

View file

@ -127,7 +127,7 @@ public:
JoltPhysicsDirectSpaceState3D *get_direct_state();
JoltArea3D *get_default_area() const { return default_area; }
void set_default_area(JoltArea3D *p_area);
void set_default_area(JoltArea3D *p_area) { default_area = p_area; }
float get_last_step() const { return last_step; }