Improve performance of changing compound shapes when using Jolt Physics

This commit is contained in:
Mikael Hermansson 2025-01-06 14:58:50 +01:00
parent 1aaf20b1f1
commit 053d92401e
7 changed files with 104 additions and 46 deletions

View file

@ -80,7 +80,7 @@ bool JoltArea3D::_has_pending_events() const {
}
void JoltArea3D::_add_to_space() {
jolt_shape = build_shape();
jolt_shape = build_shapes(true);
JPH::CollisionGroup::GroupID group_id = 0;
JPH::CollisionGroup::SubGroupID sub_group_id = 0;
@ -97,7 +97,7 @@ void JoltArea3D::_add_to_space() {
jolt_settings->mCollideKinematicVsNonDynamic = true;
}
jolt_settings->SetShape(build_shape());
jolt_settings->SetShape(jolt_shape);
const JPH::BodyID new_jolt_id = space->add_rigid_body(*this, *jolt_settings);
if (new_jolt_id.IsInvalid()) {

View file

@ -114,7 +114,7 @@ JPH::EMotionType JoltBody3D::_get_motion_type() const {
}
void JoltBody3D::_add_to_space() {
jolt_shape = build_shape();
jolt_shape = build_shapes(true);
JPH::CollisionGroup::GroupID group_id = 0;
JPH::CollisionGroup::SubGroupID sub_group_id = 0;
@ -477,8 +477,8 @@ void JoltBody3D::_mode_changed() {
wake_up();
}
void JoltBody3D::_shapes_built() {
JoltShapedObject3D::_shapes_built();
void JoltBody3D::_shapes_committed() {
JoltShapedObject3D::_shapes_committed();
_update_mass_properties();
_update_joint_constraints();

View file

@ -139,7 +139,7 @@ private:
void _exit_all_areas();
void _mode_changed();
virtual void _shapes_built() override;
virtual void _shapes_committed() override;
virtual void _space_changing() override;
virtual void _space_changed() override;
void _areas_changed();

View file

@ -36,6 +36,7 @@
#include "../spaces/jolt_space_3d.h"
#include "Jolt/Physics/Collision/Shape/EmptyShape.h"
#include "Jolt/Physics/Collision/Shape/MutableCompoundShape.h"
#include "Jolt/Physics/Collision/Shape/StaticCompoundShape.h"
bool JoltShapedObject3D::_is_big() const {
@ -43,7 +44,7 @@ bool JoltShapedObject3D::_is_big() const {
return get_aabb().get_longest_axis_size() >= 1000.0f;
}
JPH::ShapeRefC JoltShapedObject3D::_try_build_shape() {
JPH::ShapeRefC JoltShapedObject3D::_try_build_shape(bool p_optimize_compound) {
int built_shapes = 0;
for (JoltShapeInstance3D &shape : shapes) {
@ -56,7 +57,7 @@ JPH::ShapeRefC JoltShapedObject3D::_try_build_shape() {
return nullptr;
}
JPH::ShapeRefC result = built_shapes == 1 ? _try_build_single_shape() : _try_build_compound_shape();
JPH::ShapeRefC result = built_shapes == 1 ? _try_build_single_shape() : _try_build_compound_shape(p_optimize_compound);
if (unlikely(result == nullptr)) {
return nullptr;
}
@ -106,8 +107,12 @@ JPH::ShapeRefC JoltShapedObject3D::_try_build_single_shape() {
return nullptr;
}
JPH::ShapeRefC JoltShapedObject3D::_try_build_compound_shape() {
JPH::StaticCompoundShapeSettings compound_shape_settings;
JPH::ShapeRefC JoltShapedObject3D::_try_build_compound_shape(bool p_optimize) {
JPH::StaticCompoundShapeSettings static_compound_shape_settings;
JPH::MutableCompoundShapeSettings mutable_compound_shape_settings;
JPH::CompoundShapeSettings *compound_shape_settings = p_optimize ? static_cast<JPH::CompoundShapeSettings *>(&static_compound_shape_settings) : static_cast<JPH::CompoundShapeSettings *>(&mutable_compound_shape_settings);
compound_shape_settings->mSubShapes.reserve((size_t)shapes.size());
for (int shape_index = 0; shape_index < (int)shapes.size(); ++shape_index) {
const JoltShapeInstance3D &sub_shape = shapes[shape_index];
@ -122,27 +127,41 @@ JPH::ShapeRefC JoltShapedObject3D::_try_build_compound_shape() {
const Transform3D sub_shape_transform = sub_shape.get_transform_unscaled();
if (sub_shape_scale != Vector3(1, 1, 1)) {
JOLT_ENSURE_SCALE_VALID(jolt_sub_shape, sub_shape_scale, vformat("Failed to correctly scale shape at index %d in body '%s'.", shape_index, to_string()));
JOLT_ENSURE_SCALE_VALID(jolt_sub_shape, sub_shape_scale, vformat("Failed to correctly scale shape at index %d for body '%s'.", shape_index, to_string()));
jolt_sub_shape = JoltShape3D::with_scale(jolt_sub_shape, sub_shape_scale);
}
compound_shape_settings.AddShape(to_jolt(sub_shape_transform.origin), to_jolt(sub_shape_transform.basis), jolt_sub_shape);
compound_shape_settings->AddShape(to_jolt(sub_shape_transform.origin), to_jolt(sub_shape_transform.basis), jolt_sub_shape);
}
const JPH::ShapeSettings::ShapeResult shape_result = compound_shape_settings.Create();
ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to create compound shape with sub-shape count '%d'. It returned the following error: '%s'.", (int)compound_shape_settings.mSubShapes.size(), to_godot(shape_result.GetError())));
const JPH::ShapeSettings::ShapeResult shape_result = p_optimize ? static_compound_shape_settings.Create(space->get_temp_allocator()) : mutable_compound_shape_settings.Create();
ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to create compound shape for body '%s'. It returned the following error: '%s'.", to_string(), to_godot(shape_result.GetError())));
return shape_result.Get();
}
void JoltShapedObject3D::_enqueue_needs_optimization() {
if (!needs_optimization_element.in_list()) {
space->enqueue_needs_optimization(&needs_optimization_element);
}
}
void JoltShapedObject3D::_dequeue_needs_optimization() {
if (needs_optimization_element.in_list()) {
space->dequeue_needs_optimization(&needs_optimization_element);
}
}
void JoltShapedObject3D::_shapes_changed() {
_update_shape();
commit_shapes(false);
_update_object_layer();
}
void JoltShapedObject3D::_space_changing() {
JoltObject3D::_space_changing();
_dequeue_needs_optimization();
if (space != nullptr) {
const JoltWritableBody3D body = space->write_body(jolt_id);
ERR_FAIL_COND(body.is_invalid());
@ -151,30 +170,9 @@ void JoltShapedObject3D::_space_changing() {
}
}
void JoltShapedObject3D::_update_shape() {
if (!in_space()) {
_shapes_built();
return;
}
const JoltWritableBody3D body = space->write_body(jolt_id);
ERR_FAIL_COND(body.is_invalid());
JPH::ShapeRefC new_shape = build_shape();
if (new_shape == jolt_shape) {
return;
}
previous_jolt_shape = jolt_shape;
jolt_shape = new_shape;
space->get_body_iface().SetShape(jolt_id, jolt_shape, false, JPH::EActivation::DontActivate);
_shapes_built();
}
JoltShapedObject3D::JoltShapedObject3D(ObjectType p_object_type) :
JoltObject3D(p_object_type) {
JoltObject3D(p_object_type),
needs_optimization_element(this) {
jolt_settings->mAllowSleeping = true;
jolt_settings->mFriction = 1.0f;
jolt_settings->mRestitution = 0.0f;
@ -286,8 +284,8 @@ AABB JoltShapedObject3D::get_aabb() const {
return get_transform_scaled().xform(result);
}
JPH::ShapeRefC JoltShapedObject3D::build_shape() {
JPH::ShapeRefC new_shape = _try_build_shape();
JPH::ShapeRefC JoltShapedObject3D::build_shapes(bool p_optimize_compound) {
JPH::ShapeRefC new_shape = _try_build_shape(p_optimize_compound);
if (new_shape == nullptr) {
if (has_custom_center_of_mass()) {
@ -300,6 +298,34 @@ JPH::ShapeRefC JoltShapedObject3D::build_shape() {
return new_shape;
}
void JoltShapedObject3D::commit_shapes(bool p_optimize_compound) {
if (!in_space()) {
_shapes_committed();
return;
}
const JoltWritableBody3D body = space->write_body(jolt_id);
ERR_FAIL_COND(body.is_invalid());
JPH::ShapeRefC new_shape = build_shapes(p_optimize_compound);
if (new_shape == jolt_shape) {
return;
}
previous_jolt_shape = jolt_shape;
jolt_shape = new_shape;
space->get_body_iface().SetShape(jolt_id, jolt_shape, false, JPH::EActivation::DontActivate);
if (!p_optimize_compound && jolt_shape->GetType() == JPH::EShapeType::Compound) {
_enqueue_needs_optimization();
} else {
_dequeue_needs_optimization();
}
_shapes_committed();
}
void JoltShapedObject3D::add_shape(JoltShape3D *p_shape, Transform3D p_transform, bool p_disabled) {
JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, vformat("An invalid transform was passed when adding shape at index %d to physics body '%s'.", shapes.size(), to_string()));

View file

@ -33,6 +33,8 @@
#include "jolt_object_3d.h"
#include "core/templates/self_list.h"
#include "Jolt/Jolt.h"
#include "Jolt/Physics/Body/Body.h"
@ -42,6 +44,8 @@ class JoltShapedObject3D : public JoltObject3D {
friend class JoltShape3D;
protected:
SelfList<JoltShapedObject3D> needs_optimization_element;
Vector3 scale = Vector3(1, 1, 1);
JPH::ShapeRefC jolt_shape;
@ -53,16 +57,17 @@ protected:
bool _is_big() const;
JPH::ShapeRefC _try_build_shape();
JPH::ShapeRefC _try_build_shape(bool p_optimize_compound);
JPH::ShapeRefC _try_build_single_shape();
JPH::ShapeRefC _try_build_compound_shape();
JPH::ShapeRefC _try_build_compound_shape(bool p_optimize);
void _enqueue_needs_optimization();
void _dequeue_needs_optimization();
virtual void _shapes_changed();
virtual void _shapes_built() {}
virtual void _shapes_committed() {}
virtual void _space_changing() override;
void _update_shape();
public:
explicit JoltShapedObject3D(ObjectType p_object_type);
virtual ~JoltShapedObject3D() override;
@ -86,7 +91,9 @@ public:
virtual bool has_custom_center_of_mass() const = 0;
virtual Vector3 get_center_of_mass_custom() const = 0;
JPH::ShapeRefC build_shape();
JPH::ShapeRefC build_shapes(bool p_optimize_compound);
void commit_shapes(bool p_optimize_compound);
const JPH::Shape *get_jolt_shape() const { return jolt_shape; }
const JPH::Shape *get_previous_jolt_shape() const { return previous_jolt_shape; }

View file

@ -63,6 +63,12 @@ constexpr double DEFAULT_SOLVER_ITERATIONS = 8;
} // namespace
void JoltSpace3D::_pre_step(float p_step) {
while (needs_optimization_list.first()) {
JoltShapedObject3D *object = needs_optimization_list.first()->self();
needs_optimization_list.remove(needs_optimization_list.first());
object->commit_shapes(true);
}
contact_listener->pre_step();
const JPH::BodyLockInterface &lock_iface = get_lock_iface();
@ -427,6 +433,18 @@ void JoltSpace3D::dequeue_call_queries(SelfList<JoltArea3D> *p_area) {
}
}
void JoltSpace3D::enqueue_needs_optimization(SelfList<JoltShapedObject3D> *p_object) {
if (!p_object->in_list()) {
needs_optimization_list.add(p_object);
}
}
void JoltSpace3D::dequeue_needs_optimization(SelfList<JoltShapedObject3D> *p_object) {
if (p_object->in_list()) {
needs_optimization_list.remove(p_object);
}
}
void JoltSpace3D::add_joint(JPH::Constraint *p_jolt_ref) {
physics_system->AddConstraint(p_jolt_ref);
}

View file

@ -54,10 +54,12 @@ class JoltJoint3D;
class JoltLayers;
class JoltObject3D;
class JoltPhysicsDirectSpaceState3D;
class JoltShapedObject3D;
class JoltSpace3D {
SelfList<JoltBody3D>::List body_call_queries_list;
SelfList<JoltArea3D>::List area_call_queries_list;
SelfList<JoltShapedObject3D>::List needs_optimization_list;
RID rid;
@ -100,6 +102,8 @@ public:
JPH::PhysicsSystem &get_physics_system() const { return *physics_system; }
JPH::TempAllocator &get_temp_allocator() const { return *temp_allocator; }
JPH::BodyInterface &get_body_iface();
const JPH::BodyInterface &get_body_iface() const;
const JPH::BodyLockInterface &get_lock_iface() const;
@ -138,6 +142,9 @@ public:
void dequeue_call_queries(SelfList<JoltBody3D> *p_body);
void dequeue_call_queries(SelfList<JoltArea3D> *p_area);
void enqueue_needs_optimization(SelfList<JoltShapedObject3D> *p_object);
void dequeue_needs_optimization(SelfList<JoltShapedObject3D> *p_object);
void add_joint(JPH::Constraint *p_jolt_ref);
void add_joint(JoltJoint3D *p_joint);
void remove_joint(JPH::Constraint *p_jolt_ref);