Skip to content

Commit

Permalink
Improve performance of changing compound shapes when using Jolt Physics
Browse files Browse the repository at this point in the history
  • Loading branch information
mihe committed Jan 6, 2025
1 parent 1aaf20b commit 053d924
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 46 deletions.
4 changes: 2 additions & 2 deletions modules/jolt_physics/objects/jolt_area_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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()) {
Expand Down
6 changes: 3 additions & 3 deletions modules/jolt_physics/objects/jolt_body_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion modules/jolt_physics/objects/jolt_body_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class JoltBody3D final : public JoltShapedObject3D {
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();
Expand Down
94 changes: 60 additions & 34 deletions modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@
#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 {
// This number is completely arbitrary, and mostly just needs to capture `WorldBoundaryShape3D`, which needs to be kept out of the normal broadphase layers.
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) {
Expand All @@ -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;
}
Expand Down Expand Up @@ -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];
Expand All @@ -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());
Expand All @@ -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;
Expand Down Expand Up @@ -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()) {
Expand All @@ -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()));

Expand Down
19 changes: 13 additions & 6 deletions modules/jolt_physics/objects/jolt_shaped_object_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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;
Expand All @@ -53,16 +57,17 @@ class JoltShapedObject3D : public JoltObject3D {

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;
Expand All @@ -86,7 +91,9 @@ class JoltShapedObject3D : public JoltObject3D {
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; }
Expand Down
18 changes: 18 additions & 0 deletions modules/jolt_physics/spaces/jolt_space_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
Expand Down
7 changes: 7 additions & 0 deletions modules/jolt_physics/spaces/jolt_space_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -100,6 +102,8 @@ class JoltSpace3D {

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;
Expand Down Expand Up @@ -138,6 +142,9 @@ class JoltSpace3D {
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);
Expand Down

0 comments on commit 053d924

Please sign in to comment.