Path: blob/master/modules/jolt_physics/spaces/jolt_space_3d.cpp
10278 views
/**************************************************************************/1/* jolt_space_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "jolt_space_3d.h"3132#include "../joints/jolt_joint_3d.h"33#include "../jolt_physics_server_3d.h"34#include "../jolt_project_settings.h"35#include "../misc/jolt_stream_wrappers.h"36#include "../objects/jolt_area_3d.h"37#include "../objects/jolt_body_3d.h"38#include "../shapes/jolt_custom_shape_type.h"39#include "../shapes/jolt_shape_3d.h"40#include "jolt_body_activation_listener_3d.h"41#include "jolt_contact_listener_3d.h"42#include "jolt_layers.h"43#include "jolt_physics_direct_space_state_3d.h"44#include "jolt_temp_allocator.h"4546#include "core/io/file_access.h"47#include "core/os/time.h"48#include "core/string/print_string.h"49#include "core/variant/variant_utility.h"5051#include "Jolt/Physics/Collision/CollideShapeVsShapePerLeaf.h"52#include "Jolt/Physics/Collision/CollisionCollectorImpl.h"53#include "Jolt/Physics/PhysicsScene.h"5455namespace {5657constexpr double SPACE_DEFAULT_CONTACT_RECYCLE_RADIUS = 0.01;58constexpr double SPACE_DEFAULT_CONTACT_MAX_SEPARATION = 0.05;59constexpr double SPACE_DEFAULT_CONTACT_MAX_ALLOWED_PENETRATION = 0.01;60constexpr double SPACE_DEFAULT_CONTACT_DEFAULT_BIAS = 0.8;61constexpr double SPACE_DEFAULT_SLEEP_THRESHOLD_LINEAR = 0.1;62constexpr double SPACE_DEFAULT_SLEEP_THRESHOLD_ANGULAR = 8.0 * Math::PI / 180;63constexpr double SPACE_DEFAULT_SOLVER_ITERATIONS = 8;6465} // namespace6667void JoltSpace3D::_pre_step(float p_step) {68flush_pending_objects();6970while (needs_optimization_list.first()) {71JoltShapedObject3D *object = needs_optimization_list.first()->self();72needs_optimization_list.remove(needs_optimization_list.first());73object->commit_shapes(true);74}7576contact_listener->pre_step();7778const JPH::BodyLockInterface &lock_iface = get_lock_iface();79const JPH::BodyID *active_rigid_bodies = physics_system->GetActiveBodiesUnsafe(JPH::EBodyType::RigidBody);80const JPH::uint32 active_rigid_body_count = physics_system->GetNumActiveBodies(JPH::EBodyType::RigidBody);8182for (JPH::uint32 i = 0; i < active_rigid_body_count; i++) {83JPH::Body *jolt_body = lock_iface.TryGetBody(active_rigid_bodies[i]);84JoltObject3D *object = reinterpret_cast<JoltObject3D *>(jolt_body->GetUserData());85object->pre_step(p_step, *jolt_body);86}87}8889void JoltSpace3D::_post_step(float p_step) {90contact_listener->post_step();9192while (shapes_changed_list.first()) {93JoltShapedObject3D *object = shapes_changed_list.first()->self();94shapes_changed_list.remove(shapes_changed_list.first());95object->clear_previous_shape();96}97}9899JoltSpace3D::JoltSpace3D(JPH::JobSystem *p_job_system) :100job_system(p_job_system),101temp_allocator(new JoltTempAllocator()),102layers(new JoltLayers()),103contact_listener(new JoltContactListener3D(this)),104body_activation_listener(new JoltBodyActivationListener3D()),105physics_system(new JPH::PhysicsSystem()) {106physics_system->Init((JPH::uint)JoltProjectSettings::max_bodies, 0, (JPH::uint)JoltProjectSettings::max_body_pairs, (JPH::uint)JoltProjectSettings::max_contact_constraints, *layers, *layers, *layers);107108JPH::PhysicsSettings settings;109settings.mBaumgarte = JoltProjectSettings::baumgarte_stabilization_factor;110settings.mSpeculativeContactDistance = JoltProjectSettings::speculative_contact_distance;111settings.mPenetrationSlop = JoltProjectSettings::penetration_slop;112settings.mLinearCastThreshold = JoltProjectSettings::ccd_movement_threshold;113settings.mLinearCastMaxPenetration = JoltProjectSettings::ccd_max_penetration;114settings.mBodyPairCacheMaxDeltaPositionSq = JoltProjectSettings::body_pair_cache_distance_sq;115settings.mBodyPairCacheCosMaxDeltaRotationDiv2 = JoltProjectSettings::body_pair_cache_angle_cos_div2;116settings.mNumVelocitySteps = (JPH::uint)JoltProjectSettings::simulation_velocity_steps;117settings.mNumPositionSteps = (JPH::uint)JoltProjectSettings::simulation_position_steps;118settings.mMinVelocityForRestitution = JoltProjectSettings::bounce_velocity_threshold;119settings.mTimeBeforeSleep = JoltProjectSettings::sleep_time_threshold;120settings.mPointVelocitySleepThreshold = JoltProjectSettings::sleep_velocity_threshold;121settings.mUseBodyPairContactCache = JoltProjectSettings::body_pair_contact_cache_enabled;122settings.mAllowSleeping = JoltProjectSettings::sleep_allowed;123124physics_system->SetPhysicsSettings(settings);125physics_system->SetGravity(JPH::Vec3::sZero());126physics_system->SetContactListener(contact_listener);127physics_system->SetSoftBodyContactListener(contact_listener);128physics_system->SetBodyActivationListener(body_activation_listener);129130physics_system->SetSimCollideBodyVsBody([](const JPH::Body &p_body1, const JPH::Body &p_body2, JPH::Mat44Arg p_transform_com1, JPH::Mat44Arg p_transform_com2, JPH::CollideShapeSettings &p_collide_shape_settings, JPH::CollideShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) {131if (p_body1.IsSensor() || p_body2.IsSensor()) {132JPH::CollideShapeSettings new_collide_shape_settings = p_collide_shape_settings;133// Since we're breaking the sensor down into leaf shapes we'll end up stripping away our `JoltCustomDoubleSidedShape` decorator shape and thus any back-face collision, so we simply force-enable it like this rather than going through the trouble of reapplying the decorator.134new_collide_shape_settings.mBackFaceMode = JPH::EBackFaceMode::CollideWithBackFaces;135JPH::SubShapeIDCreator part1, part2;136JPH::CollideShapeVsShapePerLeaf<JPH::AnyHitCollisionCollector<JPH::CollideShapeCollector>>(p_body1.GetShape(), p_body2.GetShape(), JPH::Vec3::sOne(), JPH::Vec3::sOne(), p_transform_com1, p_transform_com2, part1, part2, new_collide_shape_settings, p_collector, p_shape_filter);137} else {138JPH::PhysicsSystem::sDefaultSimCollideBodyVsBody(p_body1, p_body2, p_transform_com1, p_transform_com2, p_collide_shape_settings, p_collector, p_shape_filter);139}140});141142physics_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) {143return Math::abs(MIN(p_body1.GetFriction(), p_body2.GetFriction()));144});145146physics_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) {147return CLAMP(p_body1.GetRestitution() + p_body2.GetRestitution(), 0.0f, 1.0f);148});149}150151JoltSpace3D::~JoltSpace3D() {152if (direct_state != nullptr) {153memdelete(direct_state);154direct_state = nullptr;155}156157if (physics_system != nullptr) {158delete physics_system;159physics_system = nullptr;160}161162if (body_activation_listener != nullptr) {163delete body_activation_listener;164body_activation_listener = nullptr;165}166167if (contact_listener != nullptr) {168delete contact_listener;169contact_listener = nullptr;170}171172if (layers != nullptr) {173delete layers;174layers = nullptr;175}176177if (temp_allocator != nullptr) {178delete temp_allocator;179temp_allocator = nullptr;180}181}182183void JoltSpace3D::step(float p_step) {184stepping = true;185last_step = p_step;186187_pre_step(p_step);188189const JPH::EPhysicsUpdateError update_error = physics_system->Update(p_step, 1, temp_allocator, job_system);190191if ((update_error & JPH::EPhysicsUpdateError::ManifoldCacheFull) != JPH::EPhysicsUpdateError::None) {192WARN_PRINT_ONCE(vformat("Jolt Physics manifold cache exceeded capacity and contacts were ignored. "193"Consider increasing maximum number of contact constraints in project settings. "194"Maximum number of contact constraints is currently set to %d.",195JoltProjectSettings::max_contact_constraints));196}197198if ((update_error & JPH::EPhysicsUpdateError::BodyPairCacheFull) != JPH::EPhysicsUpdateError::None) {199WARN_PRINT_ONCE(vformat("Jolt Physics body pair cache exceeded capacity and contacts were ignored. "200"Consider increasing maximum number of body pairs in project settings. "201"Maximum number of body pairs is currently set to %d.",202JoltProjectSettings::max_body_pairs));203}204205if ((update_error & JPH::EPhysicsUpdateError::ContactConstraintsFull) != JPH::EPhysicsUpdateError::None) {206WARN_PRINT_ONCE(vformat("Jolt Physics contact constraint buffer exceeded capacity and contacts were ignored. "207"Consider increasing maximum number of contact constraints in project settings. "208"Maximum number of contact constraints is currently set to %d.",209JoltProjectSettings::max_contact_constraints));210}211212_post_step(p_step);213214stepping = false;215}216217void JoltSpace3D::call_queries() {218while (body_call_queries_list.first()) {219JoltBody3D *body = body_call_queries_list.first()->self();220body_call_queries_list.remove(body_call_queries_list.first());221body->call_queries();222}223224while (area_call_queries_list.first()) {225JoltArea3D *body = area_call_queries_list.first()->self();226area_call_queries_list.remove(area_call_queries_list.first());227body->call_queries();228}229}230231double JoltSpace3D::get_param(PhysicsServer3D::SpaceParameter p_param) const {232switch (p_param) {233case PhysicsServer3D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: {234return SPACE_DEFAULT_CONTACT_RECYCLE_RADIUS;235}236case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_SEPARATION: {237return SPACE_DEFAULT_CONTACT_MAX_SEPARATION;238}239case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION: {240return SPACE_DEFAULT_CONTACT_MAX_ALLOWED_PENETRATION;241}242case PhysicsServer3D::SPACE_PARAM_CONTACT_DEFAULT_BIAS: {243return SPACE_DEFAULT_CONTACT_DEFAULT_BIAS;244}245case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD: {246return SPACE_DEFAULT_SLEEP_THRESHOLD_LINEAR;247}248case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD: {249return SPACE_DEFAULT_SLEEP_THRESHOLD_ANGULAR;250}251case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP: {252return JoltProjectSettings::sleep_time_threshold;253}254case PhysicsServer3D::SPACE_PARAM_SOLVER_ITERATIONS: {255return SPACE_DEFAULT_SOLVER_ITERATIONS;256}257default: {258ERR_FAIL_V_MSG(0.0, vformat("Unhandled space parameter: '%d'. This should not happen. Please report this.", p_param));259}260}261}262263void JoltSpace3D::set_param(PhysicsServer3D::SpaceParameter p_param, double p_value) {264switch (p_param) {265case PhysicsServer3D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: {266WARN_PRINT("Space-specific contact recycle radius is not supported when using Jolt Physics. Any such value will be ignored.");267} break;268case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_SEPARATION: {269WARN_PRINT("Space-specific contact max separation is not supported when using Jolt Physics. Any such value will be ignored.");270} break;271case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION: {272WARN_PRINT("Space-specific contact max allowed penetration is not supported when using Jolt Physics. Any such value will be ignored.");273} break;274case PhysicsServer3D::SPACE_PARAM_CONTACT_DEFAULT_BIAS: {275WARN_PRINT("Space-specific contact default bias is not supported when using Jolt Physics. Any such value will be ignored.");276} break;277case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD: {278WARN_PRINT("Space-specific linear velocity sleep threshold is not supported when using Jolt Physics. Any such value will be ignored.");279} break;280case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD: {281WARN_PRINT("Space-specific angular velocity sleep threshold is not supported when using Jolt Physics. Any such value will be ignored.");282} break;283case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP: {284WARN_PRINT("Space-specific body sleep time is not supported when using Jolt Physics. Any such value will be ignored.");285} break;286case PhysicsServer3D::SPACE_PARAM_SOLVER_ITERATIONS: {287WARN_PRINT("Space-specific solver iterations is not supported when using Jolt Physics. Any such value will be ignored.");288} break;289default: {290ERR_FAIL_MSG(vformat("Unhandled space parameter: '%d'. This should not happen. Please report this.", p_param));291} break;292}293}294295JPH::BodyInterface &JoltSpace3D::get_body_iface() {296return physics_system->GetBodyInterfaceNoLock();297}298299const JPH::BodyInterface &JoltSpace3D::get_body_iface() const {300return physics_system->GetBodyInterfaceNoLock();301}302303const JPH::BodyLockInterface &JoltSpace3D::get_lock_iface() const {304return physics_system->GetBodyLockInterfaceNoLock();305}306307const JPH::BroadPhaseQuery &JoltSpace3D::get_broad_phase_query() const {308return physics_system->GetBroadPhaseQuery();309}310311const JPH::NarrowPhaseQuery &JoltSpace3D::get_narrow_phase_query() const {312return physics_system->GetNarrowPhaseQueryNoLock();313}314315JPH::ObjectLayer JoltSpace3D::map_to_object_layer(JPH::BroadPhaseLayer p_broad_phase_layer, uint32_t p_collision_layer, uint32_t p_collision_mask) {316return layers->to_object_layer(p_broad_phase_layer, p_collision_layer, p_collision_mask);317}318319void JoltSpace3D::map_from_object_layer(JPH::ObjectLayer p_object_layer, JPH::BroadPhaseLayer &r_broad_phase_layer, uint32_t &r_collision_layer, uint32_t &r_collision_mask) const {320layers->from_object_layer(p_object_layer, r_broad_phase_layer, r_collision_layer, r_collision_mask);321}322323JPH::Body *JoltSpace3D::try_get_jolt_body(const JPH::BodyID &p_body_id) const {324return get_lock_iface().TryGetBody(p_body_id);325}326327JoltObject3D *JoltSpace3D::try_get_object(const JPH::BodyID &p_body_id) const {328const JPH::Body *jolt_body = try_get_jolt_body(p_body_id);329if (unlikely(jolt_body == nullptr)) {330return nullptr;331}332333return reinterpret_cast<JoltObject3D *>(jolt_body->GetUserData());334}335336JoltShapedObject3D *JoltSpace3D::try_get_shaped(const JPH::BodyID &p_body_id) const {337JoltObject3D *object = try_get_object(p_body_id);338if (unlikely(object == nullptr)) {339return nullptr;340}341342return object->as_shaped();343}344345JoltBody3D *JoltSpace3D::try_get_body(const JPH::BodyID &p_body_id) const {346JoltObject3D *object = try_get_object(p_body_id);347if (unlikely(object == nullptr)) {348return nullptr;349}350351return object->as_body();352}353354JoltArea3D *JoltSpace3D::try_get_area(const JPH::BodyID &p_body_id) const {355JoltObject3D *object = try_get_object(p_body_id);356if (unlikely(object == nullptr)) {357return nullptr;358}359360return object->as_area();361}362363JoltSoftBody3D *JoltSpace3D::try_get_soft_body(const JPH::BodyID &p_body_id) const {364JoltObject3D *object = try_get_object(p_body_id);365if (unlikely(object == nullptr)) {366return nullptr;367}368369return object->as_soft_body();370}371372JoltPhysicsDirectSpaceState3D *JoltSpace3D::get_direct_state() {373if (direct_state == nullptr) {374direct_state = memnew(JoltPhysicsDirectSpaceState3D(this));375}376377return direct_state;378}379380void JoltSpace3D::set_default_area(JoltArea3D *p_area) {381if (default_area == p_area) {382return;383}384385if (default_area != nullptr) {386default_area->set_default_area(false);387}388389default_area = p_area;390391if (default_area != nullptr) {392default_area->set_default_area(true);393}394}395396JPH::Body *JoltSpace3D::add_object(const JoltObject3D &p_object, const JPH::BodyCreationSettings &p_settings, bool p_sleeping) {397JPH::BodyInterface &body_iface = get_body_iface();398JPH::Body *jolt_body = body_iface.CreateBody(p_settings);399if (unlikely(jolt_body == nullptr)) {400ERR_PRINT_ONCE(vformat("Failed to create underlying Jolt Physics body for '%s'. "401"Consider increasing maximum number of bodies in project settings. "402"Maximum number of bodies is currently set to %d.",403p_object.to_string(), JoltProjectSettings::max_bodies));404405return nullptr;406}407408if (p_sleeping) {409pending_objects_sleeping.push_back(jolt_body->GetID());410} else {411pending_objects_awake.push_back(jolt_body->GetID());412}413414return jolt_body;415}416417JPH::Body *JoltSpace3D::add_object(const JoltObject3D &p_object, const JPH::SoftBodyCreationSettings &p_settings, bool p_sleeping) {418JPH::BodyInterface &body_iface = get_body_iface();419JPH::Body *jolt_body = body_iface.CreateSoftBody(p_settings);420if (unlikely(jolt_body == nullptr)) {421ERR_PRINT_ONCE(vformat("Failed to create underlying Jolt Physics body for '%s'. "422"Consider increasing maximum number of bodies in project settings. "423"Maximum number of bodies is currently set to %d.",424p_object.to_string(), JoltProjectSettings::max_bodies));425426return nullptr;427}428429if (p_sleeping) {430pending_objects_sleeping.push_back(jolt_body->GetID());431} else {432pending_objects_awake.push_back(jolt_body->GetID());433}434435return jolt_body;436}437438void JoltSpace3D::remove_object(const JPH::BodyID &p_jolt_id) {439JPH::BodyInterface &body_iface = get_body_iface();440441if (!pending_objects_sleeping.erase_unordered(p_jolt_id) && !pending_objects_awake.erase_unordered(p_jolt_id)) {442body_iface.RemoveBody(p_jolt_id);443}444445body_iface.DestroyBody(p_jolt_id);446447// If we're never going to step this space, like in the editor viewport, we need to manually clean up Jolt's broad phase instead, otherwise performance can degrade when doing things like switching scenes.448// We'll never actually have zero bodies in any space though, since we always have the default area, so we check if there's one or fewer left instead.449if (!JoltPhysicsServer3D::get_singleton()->is_active() && physics_system->GetNumBodies() <= 1) {450physics_system->OptimizeBroadPhase();451}452}453454void JoltSpace3D::flush_pending_objects() {455if (pending_objects_sleeping.is_empty() && pending_objects_awake.is_empty()) {456return;457}458459// We only care about locking within this method, because it's called when performing queries, which aren't covered by `PhysicsServer3DWrapMT`.460MutexLock pending_objects_lock(pending_objects_mutex);461462JPH::BodyInterface &body_iface = get_body_iface();463464if (!pending_objects_sleeping.is_empty()) {465JPH::BodyInterface::AddState add_state = body_iface.AddBodiesPrepare(pending_objects_sleeping.ptr(), pending_objects_sleeping.size());466body_iface.AddBodiesFinalize(pending_objects_sleeping.ptr(), pending_objects_sleeping.size(), add_state, JPH::EActivation::DontActivate);467pending_objects_sleeping.reset();468}469470if (!pending_objects_awake.is_empty()) {471JPH::BodyInterface::AddState add_state = body_iface.AddBodiesPrepare(pending_objects_awake.ptr(), pending_objects_awake.size());472body_iface.AddBodiesFinalize(pending_objects_awake.ptr(), pending_objects_awake.size(), add_state, JPH::EActivation::Activate);473pending_objects_awake.reset();474}475}476477void JoltSpace3D::set_is_object_sleeping(const JPH::BodyID &p_jolt_id, bool p_enable) {478if (p_enable) {479if (pending_objects_awake.erase_unordered(p_jolt_id)) {480pending_objects_sleeping.push_back(p_jolt_id);481} else if (pending_objects_sleeping.has(p_jolt_id)) {482// Do nothing.483} else {484get_body_iface().DeactivateBody(p_jolt_id);485}486} else {487if (pending_objects_sleeping.erase_unordered(p_jolt_id)) {488pending_objects_awake.push_back(p_jolt_id);489} else if (pending_objects_awake.has(p_jolt_id)) {490// Do nothing.491} else {492get_body_iface().ActivateBody(p_jolt_id);493}494}495}496497void JoltSpace3D::enqueue_call_queries(SelfList<JoltBody3D> *p_body) {498// This method will be called from the body activation listener on multiple threads during the simulation step.499MutexLock body_call_queries_lock(body_call_queries_mutex);500501if (!p_body->in_list()) {502body_call_queries_list.add(p_body);503}504}505506void JoltSpace3D::enqueue_call_queries(SelfList<JoltArea3D> *p_area) {507if (!p_area->in_list()) {508area_call_queries_list.add(p_area);509}510}511512void JoltSpace3D::dequeue_call_queries(SelfList<JoltBody3D> *p_body) {513if (p_body->in_list()) {514body_call_queries_list.remove(p_body);515}516}517518void JoltSpace3D::dequeue_call_queries(SelfList<JoltArea3D> *p_area) {519if (p_area->in_list()) {520area_call_queries_list.remove(p_area);521}522}523524void JoltSpace3D::enqueue_shapes_changed(SelfList<JoltShapedObject3D> *p_object) {525if (!p_object->in_list()) {526shapes_changed_list.add(p_object);527}528}529530void JoltSpace3D::dequeue_shapes_changed(SelfList<JoltShapedObject3D> *p_object) {531if (p_object->in_list()) {532shapes_changed_list.remove(p_object);533}534}535536void JoltSpace3D::enqueue_needs_optimization(SelfList<JoltShapedObject3D> *p_object) {537if (!p_object->in_list()) {538needs_optimization_list.add(p_object);539}540}541542void JoltSpace3D::dequeue_needs_optimization(SelfList<JoltShapedObject3D> *p_object) {543if (p_object->in_list()) {544needs_optimization_list.remove(p_object);545}546}547548void JoltSpace3D::add_joint(JPH::Constraint *p_jolt_ref) {549physics_system->AddConstraint(p_jolt_ref);550}551552void JoltSpace3D::add_joint(JoltJoint3D *p_joint) {553add_joint(p_joint->get_jolt_ref());554}555556void JoltSpace3D::remove_joint(JPH::Constraint *p_jolt_ref) {557physics_system->RemoveConstraint(p_jolt_ref);558}559560void JoltSpace3D::remove_joint(JoltJoint3D *p_joint) {561remove_joint(p_joint->get_jolt_ref());562}563564#ifdef DEBUG_ENABLED565566void JoltSpace3D::dump_debug_snapshot(const String &p_dir) {567const Dictionary datetime = Time::get_singleton()->get_datetime_dict_from_system();568const String datetime_str = vformat("%04d-%02d-%02d_%02d-%02d-%02d", datetime["year"], datetime["month"], datetime["day"], datetime["hour"], datetime["minute"], datetime["second"]);569const String path = p_dir + vformat("/jolt_snapshot_%s_%d.bin", datetime_str, rid.get_id());570571Ref<FileAccess> file_access = FileAccess::open(path, FileAccess::ModeFlags::WRITE);572ERR_FAIL_COND_MSG(file_access.is_null(), vformat("Failed to open '%s' for writing when saving snapshot of physics space with RID '%d'.", path, rid.get_id()));573574JPH::PhysicsScene physics_scene;575physics_scene.FromPhysicsSystem(physics_system);576577for (JPH::BodyCreationSettings &settings : physics_scene.GetBodies()) {578const JoltObject3D *object = reinterpret_cast<const JoltObject3D *>(settings.mUserData);579580if (const JoltBody3D *body = object->as_body()) {581// 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.582settings.mGravityFactor = body->get_gravity_scale();583settings.mLinearDamping = body->get_total_linear_damp();584settings.mAngularDamping = body->get_total_angular_damp();585}586587settings.SetShape(JoltShape3D::without_custom_shapes(settings.GetShape()));588}589590JoltStreamOutputWrapper output_stream(file_access);591physics_scene.SaveBinaryState(output_stream, true, false);592593ERR_FAIL_COND_MSG(file_access->get_error() != OK, vformat("Writing snapshot of physics space with RID '%d' to '%s' failed with error '%s'.", rid.get_id(), path, VariantUtilityFunctions::error_string(file_access->get_error())));594595print_line(vformat("Snapshot of physics space with RID '%d' saved to '%s'.", rid.get_id(), path));596}597598const PackedVector3Array &JoltSpace3D::get_debug_contacts() const {599return contact_listener->get_debug_contacts();600}601602int JoltSpace3D::get_debug_contact_count() const {603return contact_listener->get_debug_contact_count();604}605606int JoltSpace3D::get_max_debug_contacts() const {607return contact_listener->get_max_debug_contacts();608}609610void JoltSpace3D::set_max_debug_contacts(int p_count) {611contact_listener->set_max_debug_contacts(p_count);612}613614#endif615616617