diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 5cc6f7971b55..0498f335cf1b 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -101,6 +101,13 @@ Copyright: 2007, Starbreeze Studios
2007-2014, Juan Linietsky, Ariel Manzur
License: Expat and Zlib
+Files: ./modules/jolt_physics/spaces/jolt_temp_allocator.cpp
+Comment: Jolt Physics
+Copyright: 2021, Jorrit Rouwe
+ 2014-present, Godot Engine contributors
+ 2007-2014, Juan Linietsky, Ariel Manzur
+License: Expat
+
Files: ./modules/lightmapper_rd/lm_compute.glsl
Comment: Joint Non-Local Means (JNLM) denoiser
Copyright: 2020, Manuel Prandini
@@ -289,6 +296,11 @@ Comment: International Components for Unicode
Copyright: 2016-2024, Unicode, Inc.
License: Unicode
+Files: ./thirdparty/jolt_physics/
+Comment: Jolt Physics
+Copyright: 2021, Jorrit Rouwe
+License: Expat
+
Files: ./thirdparty/jpeg-compressor/
Comment: jpeg-compressor
Copyright: 2012, Rich Geldreich
diff --git a/core/os/memory.h b/core/os/memory.h
index 033e417cb57f..0071c3d514fe 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -122,10 +122,10 @@ _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
return p_obj;
}
-#define memnew(m_class) _post_initialize(new ("") m_class)
+#define memnew(m_class) _post_initialize(::new ("") m_class)
-#define memnew_allocator(m_class, m_allocator) _post_initialize(new (m_allocator::alloc) m_class)
-#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement) m_class)
+#define memnew_allocator(m_class, m_allocator) _post_initialize(::new (m_allocator::alloc) m_class)
+#define memnew_placement(m_placement, m_class) _post_initialize(::new (m_placement) m_class)
_ALWAYS_INLINE_ bool predelete_handler(void *) {
return true;
@@ -189,7 +189,7 @@ T *memnew_arr_template(size_t p_elements) {
/* call operator new */
for (size_t i = 0; i < p_elements; i++) {
- new (&elems[i]) T;
+ ::new (&elems[i]) T;
}
}
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 91961fcf02b6..85f56b9aab87 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2364,6 +2364,136 @@
[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_ticks_per_second] instead.
[b]Note:[/b] Only [member physics/common/max_physics_steps_per_frame] physics ticks may be simulated per rendered frame at most. If more physics ticks have to be simulated per rendered frame to keep up with rendering, the project will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended to also increase [member physics/common/max_physics_steps_per_frame] if increasing [member physics/common/physics_ticks_per_second] significantly above its default value.
+
+ The maximum angle between two triangles (in a [ConcavePolygonShape3D] or [HeightMapShape3D]) within which a convex edge will be considered active or inactive.
+ Collisions against an inactive edge will have its normal overridden to instead be the surface normal of the triangle. This can help alleviate ghost collisions.
+ [b]Note:[/b] Setting this too high can result in objects not depenetrating properly.
+ [b]Note:[/b] This applies to all shape queries, as well as physics bodies within the simulation.
+ [b]Note:[/b] This does not apply when enabling Jolt's enhanced internal edge removal, which supersedes this.
+
+
+ The amount of shape margin to use for certain convex collision shapes, such as [BoxShape3D], [CylinderShape3D] and [ConvexPolygonShape3D], as a fraction of the shape's shortest axis.
+ [b]Note:[/b] Shape margins in Jolt do not add any extra size to the shape, instead the shape is first shrunk by the margin and then inflated by the same amount, resulting in a shape with rounded edges. This is mainly used to speed up collision detection with convex shapes.
+ [b]Note:[/b] Setting this too low (e.g. 0) can also negatively affect the accuracy of the collision detection with convex shapes.
+
+
+ Which of the two nodes in a single-body joint (i.e. when omitting one of the nodes) should represent the world. This can be thought of as having the omitted node be a [StaticBody3D] at [code](0, 0, 0)[/code].
+ [b]Note:[/b] Godot Physics uses [code]node_b[/code] as the default.
+
+
+ The maximum angular velocity that a [RigidBody3D] can reach.
+
+
+ The maximum number of [PhysicsBody3D] to support at the same time, awake or sleeping.
+ [b]Note:[/b] When this limit is exceeded a warning is emitted and anything past that point is undefined behavior.
+ [b]Note:[/b] This limit also applies within the editor.
+
+
+ The maximum number of body pairs to allow processing of.
+ [b]Note:[/b] When this limit is exceeded collisions will randomly be ignored and bodies will pass through each other.
+
+
+ The maximum number of contact constraints to allow processing of.
+ [b]Note:[/b] When this limit is exceeded collisions will randomly be ignored and bodies will pass through each other.
+
+
+ The maximum linear velocity that a [RigidBody3D] can reach.
+
+
+ The amount of memory to pre-allocate for the stack allocator used within Jolt.
+
+
+ How wide/deep/high a [WorldBoundaryShape3D] will be.
+ [b]Note:[/b] Only half of this value will be used for its height.
+ [b]Note:[/b] Setting this too high can negatively affect the accuracy of the collision detection.
+ [b]Note:[/b] Collisions against the effective edges of a [WorldBoundaryShape3D] will be inconsistent.
+
+
+ The amount of depenetration per iteration when depenetrating during motion queries.
+ [b]Note:[/b] This applies to [method CharacterBody3D.move_and_slide], [method PhysicsBody3D.move_and_collide] and [method PhysicsServer3D.body_test_motion].
+
+
+ The number of iterations to run when depenetrating during motion queries.
+ [b]Note:[/b] This applies to [method CharacterBody3D.move_and_slide], [method PhysicsBody3D.move_and_collide] and [method PhysicsServer3D.body_test_motion].
+
+
+ Whether to use Jolt's enhanced internal edge removal during motion queries. This can help alleviate ghost collisions.
+ [b]Note:[/b] This applies to [method CharacterBody3D.move_and_slide], [method PhysicsBody3D.move_and_collide] and [method PhysicsServer3D.body_test_motion].
+ [b]Note:[/b] This will only remove edge collisions internal to a single body, meaning edges between separate bodies can still cause ghost collisions.
+
+
+ Whether to populate the [code]face_index[/code] field in the results of [method PhysicsDirectSpaceState3D.intersect_ray], also accessed through [method RayCast3D.get_collision_face_index].
+ [b]Note:[/b] Enabling this setting will increase Jolt's memory usage for [ConcavePolygonShape3D] by around 25%.
+ [b]Note:[/b] The face index will be left at its default value of [code]-1[/code] when this setting is disabled.
+
+
+ Whether to use Jolt's enhanced internal edge removal during shape queries. This can help alleviate ghost collisions when using shape queries for things like character movement.
+ [b]Note:[/b] This applies to [method PhysicsDirectSpaceState3D.cast_motion], [method PhysicsDirectSpaceState3D.collide_shape], [method PhysicsDirectSpaceState3D.get_rest_info] and [method PhysicsDirectSpaceState3D.intersect_shape].
+ [b]Note:[/b] Enabling this setting can cause certain shapes to be culled from the results entirely, but you will get at least one intersection per body.
+ [b]Note:[/b] This will only remove edge collisions internal to a single body, meaning edges between separate bodies can still cause ghost collisions.
+
+
+ Whether [RigidBody3D] is allowed to go to sleep.
+
+
+ Whether or not [Area3D] is able to detect overlaps with static physics bodies, such as [StaticBody3D].
+ [b]Note:[/b] Enabling this setting can come at a heavy CPU and memory cost if you allow many/large [Area3D] to overlap with complex static geometry, such as [ConcavePolygonShape3D] or [HeightMapShape3D]. It is strongly recommended that you set up your collision layers and masks in such a way that only a few small [Area3D] can detect static bodies.
+ [b]Note:[/b] This also applies to overlaps with [RigidBody3D] frozen with [constant RigidBody3D.FREEZE_MODE_STATIC].
+ [b]Note:[/b] This is not needed to detect overlaps with [AnimatableBody3D], which is a kinematic body.
+
+
+ How much of the position error of a [RigidBody3D] to fix during a physics step, where 0 is none and 1 is the full amount, which affect things like how quickly bodies depenetrate.
+ [b]Note:[/b] Setting this too high can result in instability for [RigidBody3D].
+
+
+ The maximum relative angle by which a body pair can move and still reuse the collision results from the previous physics step.
+
+
+ The maximum relative distance by which a body pair can move and still reuse the collision results from the previous physics step.
+
+
+ Whether the body pair contact cache is enabled, which removes the need for potentially expensive collision detection when the relative orientation between two bodies hasn't changed much.
+
+
+ The minimum velocity needed before a collision can be elastic.
+
+
+ Fraction of its inner radius a body may penetrate another body whilst using CCD.
+
+
+ Fraction of its inner radius a body must move per step to make use of CCD.
+
+
+ Whether or not a [RigidBody3D] frozen with [constant RigidBody3D.FREEZE_MODE_KINEMATIC] is able to collide with (and thus generate contacts for) other kinematic (and static) bodies.
+ [b]Note:[/b] This setting can come at a heavy CPU and memory cost if you allow many/large frozen kinematic bodies with a non-zero [member RigidBody3D.max_contacts_reported] to overlap with complex static geometry, such as [ConcavePolygonShape3D] or [HeightMapShape3D].
+
+
+ How much bodies are allowed to penetrate each other.
+
+
+ Number of solver position iterations. The greater the number of iterations, the more accurate the simulation will be, at the cost of CPU performance.
+
+
+ Time in seconds a [RigidBody3D] will spend below the sleep velocity threshold before going to sleep.
+
+
+ Velocity of certain points on the bounding box of a [RigidBody3D] below which it can be put to sleep.
+
+
+ How big the points of a [SoftBody3D] are. This can prevent things like cloth from laying perfectly flush against other surfaces and cause Z-fighting.
+
+
+ Radius around physics bodies inside which speculative contact points will be detected.
+ [b]Note:[/b] Setting this too high will result in ghost collisions, as speculative contacts are based on the closest points during the collision detection step which may not be the actual closest points by the time the two bodies hit.
+
+
+ Whether to use Jolt's enhanced internal edge removal for [RigidBody3D]. This can help alleviate ghost collisions when (for example) a [RigidBody3D] collides with the edges of two perfectly joined [BoxShape3D].
+ [b]Note:[/b] This will only remove edge collisions internal to a single body, meaning edges between separate bodies can still cause ghost collisions.
+
+
+ Number of solver velocity iterations. The greater the number of iterations, the more accurate the simulation will be, at the cost of CPU performance.
+ [b]Note:[/b] This needs to be at least [code]2[/code] in order for friction to work, as friction is applied using the non-penetration impulse from the previous iteration.
+
Maximum number of canvas item commands that can be batched into a single draw call.
diff --git a/modules/jolt_physics/SCsub b/modules/jolt_physics/SCsub
new file mode 100644
index 000000000000..e5bce1f17e6d
--- /dev/null
+++ b/modules/jolt_physics/SCsub
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+from misc.utility.scons_hints import *
+
+Import("env")
+Import("env_modules")
+
+env_jolt = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_dir = "#thirdparty/jolt_physics/"
+thirdparty_sources = [
+ "Jolt/RegisterTypes.cpp",
+ "Jolt/AABBTree/AABBTreeBuilder.cpp",
+ "Jolt/Core/Color.cpp",
+ "Jolt/Core/Factory.cpp",
+ "Jolt/Core/IssueReporting.cpp",
+ "Jolt/Core/JobSystemSingleThreaded.cpp",
+ "Jolt/Core/JobSystemThreadPool.cpp",
+ "Jolt/Core/JobSystemWithBarrier.cpp",
+ "Jolt/Core/LinearCurve.cpp",
+ "Jolt/Core/Memory.cpp",
+ "Jolt/Core/Profiler.cpp",
+ "Jolt/Core/RTTI.cpp",
+ "Jolt/Core/Semaphore.cpp",
+ "Jolt/Core/StringTools.cpp",
+ "Jolt/Core/TickCounter.cpp",
+ "Jolt/Geometry/ConvexHullBuilder.cpp",
+ "Jolt/Geometry/ConvexHullBuilder2D.cpp",
+ "Jolt/Geometry/Indexify.cpp",
+ "Jolt/Geometry/OrientedBox.cpp",
+ "Jolt/Math/Vec3.cpp",
+ # "Jolt/ObjectStream/ObjectStream.cpp",
+ # "Jolt/ObjectStream/ObjectStreamBinaryIn.cpp",
+ # "Jolt/ObjectStream/ObjectStreamBinaryOut.cpp",
+ # "Jolt/ObjectStream/ObjectStreamIn.cpp",
+ # "Jolt/ObjectStream/ObjectStreamOut.cpp",
+ # "Jolt/ObjectStream/ObjectStreamTextIn.cpp",
+ # "Jolt/ObjectStream/ObjectStreamTextOut.cpp",
+ "Jolt/ObjectStream/SerializableObject.cpp",
+ # "Jolt/ObjectStream/TypeDeclarations.cpp",
+ "Jolt/Physics/DeterminismLog.cpp",
+ "Jolt/Physics/IslandBuilder.cpp",
+ "Jolt/Physics/LargeIslandSplitter.cpp",
+ "Jolt/Physics/PhysicsScene.cpp",
+ "Jolt/Physics/PhysicsSystem.cpp",
+ "Jolt/Physics/PhysicsUpdateContext.cpp",
+ "Jolt/Physics/StateRecorderImpl.cpp",
+ "Jolt/Physics/Body/Body.cpp",
+ "Jolt/Physics/Body/BodyCreationSettings.cpp",
+ "Jolt/Physics/Body/BodyInterface.cpp",
+ "Jolt/Physics/Body/BodyManager.cpp",
+ "Jolt/Physics/Body/MassProperties.cpp",
+ "Jolt/Physics/Body/MotionProperties.cpp",
+ "Jolt/Physics/Character/Character.cpp",
+ "Jolt/Physics/Character/CharacterBase.cpp",
+ "Jolt/Physics/Character/CharacterVirtual.cpp",
+ "Jolt/Physics/Collision/CastConvexVsTriangles.cpp",
+ "Jolt/Physics/Collision/CastSphereVsTriangles.cpp",
+ "Jolt/Physics/Collision/CollideConvexVsTriangles.cpp",
+ "Jolt/Physics/Collision/CollideSphereVsTriangles.cpp",
+ "Jolt/Physics/Collision/CollisionDispatch.cpp",
+ "Jolt/Physics/Collision/CollisionGroup.cpp",
+ "Jolt/Physics/Collision/EstimateCollisionResponse.cpp",
+ "Jolt/Physics/Collision/GroupFilter.cpp",
+ "Jolt/Physics/Collision/GroupFilterTable.cpp",
+ "Jolt/Physics/Collision/ManifoldBetweenTwoFaces.cpp",
+ "Jolt/Physics/Collision/NarrowPhaseQuery.cpp",
+ "Jolt/Physics/Collision/NarrowPhaseStats.cpp",
+ "Jolt/Physics/Collision/PhysicsMaterial.cpp",
+ "Jolt/Physics/Collision/PhysicsMaterialSimple.cpp",
+ "Jolt/Physics/Collision/TransformedShape.cpp",
+ "Jolt/Physics/Collision/BroadPhase/BroadPhase.cpp",
+ "Jolt/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp",
+ "Jolt/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp",
+ "Jolt/Physics/Collision/BroadPhase/QuadTree.cpp",
+ "Jolt/Physics/Collision/Shape/BoxShape.cpp",
+ "Jolt/Physics/Collision/Shape/CapsuleShape.cpp",
+ "Jolt/Physics/Collision/Shape/CompoundShape.cpp",
+ "Jolt/Physics/Collision/Shape/ConvexHullShape.cpp",
+ "Jolt/Physics/Collision/Shape/ConvexShape.cpp",
+ "Jolt/Physics/Collision/Shape/CylinderShape.cpp",
+ "Jolt/Physics/Collision/Shape/DecoratedShape.cpp",
+ "Jolt/Physics/Collision/Shape/EmptyShape.cpp",
+ "Jolt/Physics/Collision/Shape/HeightFieldShape.cpp",
+ "Jolt/Physics/Collision/Shape/MeshShape.cpp",
+ "Jolt/Physics/Collision/Shape/MutableCompoundShape.cpp",
+ "Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.cpp",
+ "Jolt/Physics/Collision/Shape/PlaneShape.cpp",
+ "Jolt/Physics/Collision/Shape/RotatedTranslatedShape.cpp",
+ "Jolt/Physics/Collision/Shape/ScaledShape.cpp",
+ "Jolt/Physics/Collision/Shape/Shape.cpp",
+ "Jolt/Physics/Collision/Shape/SphereShape.cpp",
+ "Jolt/Physics/Collision/Shape/StaticCompoundShape.cpp",
+ "Jolt/Physics/Collision/Shape/TaperedCapsuleShape.cpp",
+ "Jolt/Physics/Collision/Shape/TaperedCylinderShape.cpp",
+ "Jolt/Physics/Collision/Shape/TriangleShape.cpp",
+ "Jolt/Physics/Constraints/ConeConstraint.cpp",
+ "Jolt/Physics/Constraints/Constraint.cpp",
+ "Jolt/Physics/Constraints/ConstraintManager.cpp",
+ "Jolt/Physics/Constraints/ContactConstraintManager.cpp",
+ "Jolt/Physics/Constraints/DistanceConstraint.cpp",
+ "Jolt/Physics/Constraints/FixedConstraint.cpp",
+ "Jolt/Physics/Constraints/GearConstraint.cpp",
+ "Jolt/Physics/Constraints/HingeConstraint.cpp",
+ "Jolt/Physics/Constraints/MotorSettings.cpp",
+ "Jolt/Physics/Constraints/PathConstraint.cpp",
+ "Jolt/Physics/Constraints/PathConstraintPath.cpp",
+ "Jolt/Physics/Constraints/PathConstraintPathHermite.cpp",
+ "Jolt/Physics/Constraints/PointConstraint.cpp",
+ "Jolt/Physics/Constraints/PulleyConstraint.cpp",
+ "Jolt/Physics/Constraints/RackAndPinionConstraint.cpp",
+ "Jolt/Physics/Constraints/SixDOFConstraint.cpp",
+ "Jolt/Physics/Constraints/SliderConstraint.cpp",
+ "Jolt/Physics/Constraints/SpringSettings.cpp",
+ "Jolt/Physics/Constraints/SwingTwistConstraint.cpp",
+ "Jolt/Physics/Constraints/TwoBodyConstraint.cpp",
+ "Jolt/Physics/Ragdoll/Ragdoll.cpp",
+ "Jolt/Physics/SoftBody/SoftBodyCreationSettings.cpp",
+ "Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp",
+ "Jolt/Physics/SoftBody/SoftBodyShape.cpp",
+ "Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp",
+ "Jolt/Physics/Vehicle/MotorcycleController.cpp",
+ "Jolt/Physics/Vehicle/TrackedVehicleController.cpp",
+ "Jolt/Physics/Vehicle/VehicleAntiRollBar.cpp",
+ "Jolt/Physics/Vehicle/VehicleCollisionTester.cpp",
+ "Jolt/Physics/Vehicle/VehicleConstraint.cpp",
+ "Jolt/Physics/Vehicle/VehicleController.cpp",
+ "Jolt/Physics/Vehicle/VehicleDifferential.cpp",
+ "Jolt/Physics/Vehicle/VehicleEngine.cpp",
+ "Jolt/Physics/Vehicle/VehicleTrack.cpp",
+ "Jolt/Physics/Vehicle/VehicleTransmission.cpp",
+ "Jolt/Physics/Vehicle/Wheel.cpp",
+ "Jolt/Physics/Vehicle/WheeledVehicleController.cpp",
+ "Jolt/Renderer/DebugRenderer.cpp",
+ "Jolt/Renderer/DebugRendererPlayback.cpp",
+ "Jolt/Renderer/DebugRendererRecorder.cpp",
+ "Jolt/Renderer/DebugRendererSimple.cpp",
+ "Jolt/Skeleton/SkeletalAnimation.cpp",
+ "Jolt/Skeleton/Skeleton.cpp",
+ "Jolt/Skeleton/SkeletonMapper.cpp",
+ "Jolt/Skeleton/SkeletonPose.cpp",
+ "Jolt/TriangleGrouper/TriangleGrouperClosestCentroid.cpp",
+ "Jolt/TriangleGrouper/TriangleGrouperMorton.cpp",
+ "Jolt/TriangleSplitter/TriangleSplitter.cpp",
+ "Jolt/TriangleSplitter/TriangleSplitterBinning.cpp",
+ "Jolt/TriangleSplitter/TriangleSplitterFixedLeafSize.cpp",
+ "Jolt/TriangleSplitter/TriangleSplitterLongestAxis.cpp",
+ "Jolt/TriangleSplitter/TriangleSplitterMean.cpp",
+ "Jolt/TriangleSplitter/TriangleSplitterMorton.cpp",
+]
+
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_jolt.Prepend(CPPPATH=[thirdparty_dir])
+
+if env.dev_build:
+ env_jolt.Append(CPPDEFINES=["JPH_ENABLE_ASSERTS"])
+
+if env.editor_build:
+ env_jolt.Append(CPPDEFINES=["JPH_DEBUG_RENDERER"])
+
+if env["precision"] == "double":
+ env_jolt.Append(CPPDEFINES=["JPH_DOUBLE_PRECISION"])
+
+env_thirdparty = env_jolt.Clone()
+env_thirdparty.disable_warnings()
+
+thirdparty_obj = []
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_jolt.add_source_files(module_obj, "*.cpp")
+env_jolt.add_source_files(module_obj, "joints/*.cpp")
+env_jolt.add_source_files(module_obj, "misc/*.cpp")
+env_jolt.add_source_files(module_obj, "objects/*.cpp")
+env_jolt.add_source_files(module_obj, "shapes/*.cpp")
+env_jolt.add_source_files(module_obj, "spaces/*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/jolt_physics/config.py b/modules/jolt_physics/config.py
new file mode 100644
index 000000000000..a42f27fbe122
--- /dev/null
+++ b/modules/jolt_physics/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return not env["disable_3d"]
+
+
+def configure(env):
+ pass
diff --git a/modules/jolt_physics/joints/jolt_cone_twist_joint_3d.cpp b/modules/jolt_physics/joints/jolt_cone_twist_joint_3d.cpp
new file mode 100644
index 000000000000..950a10a81e65
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_cone_twist_joint_3d.cpp
@@ -0,0 +1,386 @@
+/**************************************************************************/
+/* jolt_cone_twist_joint_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_cone_twist_joint_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_body_3d.h"
+#include "../spaces/jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Constraints/SwingTwistConstraint.h"
+
+namespace {
+
+constexpr double DEFAULT_BIAS = 0.3;
+constexpr double DEFAULT_SOFTNESS = 0.8;
+constexpr double DEFAULT_RELAXATION = 1.0;
+
+} // namespace
+
+JPH::Constraint *JoltConeTwistJoint3D::_build_swing_twist(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b, float p_swing_limit_span, float p_twist_limit_span) const {
+ JPH::SwingTwistConstraintSettings constraint_settings;
+
+ const bool twist_span_valid = p_twist_limit_span >= 0 && p_twist_limit_span <= JPH::JPH_PI;
+ const bool swing_span_valid = p_swing_limit_span >= 0 && p_swing_limit_span <= JPH::JPH_PI;
+
+ if (twist_limit_enabled && twist_span_valid) {
+ constraint_settings.mTwistMinAngle = -p_twist_limit_span;
+ constraint_settings.mTwistMaxAngle = p_twist_limit_span;
+ } else {
+ constraint_settings.mTwistMinAngle = -JPH::JPH_PI;
+ constraint_settings.mTwistMaxAngle = JPH::JPH_PI;
+ }
+
+ if (swing_limit_enabled && swing_span_valid) {
+ constraint_settings.mNormalHalfConeAngle = p_swing_limit_span;
+ constraint_settings.mPlaneHalfConeAngle = p_swing_limit_span;
+ } else {
+ constraint_settings.mNormalHalfConeAngle = JPH::JPH_PI;
+ constraint_settings.mPlaneHalfConeAngle = JPH::JPH_PI;
+
+ if (!swing_span_valid) {
+ constraint_settings.mTwistMinAngle = -JPH::JPH_PI;
+ constraint_settings.mTwistMaxAngle = JPH::JPH_PI;
+ }
+ }
+
+ constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
+ constraint_settings.mPosition1 = to_jolt_r(p_shifted_ref_a.origin);
+ constraint_settings.mTwistAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mPlaneAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Z));
+ constraint_settings.mPosition2 = to_jolt_r(p_shifted_ref_b.origin);
+ constraint_settings.mTwistAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mPlaneAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Z));
+ constraint_settings.mSwingType = JPH::ESwingType::Pyramid;
+
+ if (p_jolt_body_a == nullptr) {
+ return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);
+ } else if (p_jolt_body_b == nullptr) {
+ return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);
+ } else {
+ return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);
+ }
+}
+
+void JoltConeTwistJoint3D::_update_swing_motor_state() {
+ if (JPH::SwingTwistConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ constraint->SetSwingMotorState(swing_motor_enabled ? JPH::EMotorState::Velocity : JPH::EMotorState::Off);
+ }
+}
+
+void JoltConeTwistJoint3D::_update_twist_motor_state() {
+ if (JPH::SwingTwistConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ constraint->SetTwistMotorState(twist_motor_enabled ? JPH::EMotorState::Velocity : JPH::EMotorState::Off);
+ }
+}
+
+void JoltConeTwistJoint3D::_update_motor_velocity() {
+ if (JPH::SwingTwistConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ // We flip the direction since Jolt is CCW but Godot is CW.
+ constraint->SetTargetAngularVelocityCS({ (float)-twist_motor_target_speed, (float)-swing_motor_target_speed_y, (float)-swing_motor_target_speed_z });
+ }
+}
+
+void JoltConeTwistJoint3D::_update_swing_motor_limit() {
+ if (JPH::SwingTwistConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ JPH::MotorSettings &motor_settings = constraint->GetSwingMotorSettings();
+ motor_settings.mMinTorqueLimit = (float)-swing_motor_max_torque;
+ motor_settings.mMaxTorqueLimit = (float)swing_motor_max_torque;
+ }
+}
+
+void JoltConeTwistJoint3D::_update_twist_motor_limit() {
+ if (JPH::SwingTwistConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ JPH::MotorSettings &motor_settings = constraint->GetTwistMotorSettings();
+ motor_settings.mMinTorqueLimit = (float)-twist_motor_max_torque;
+ motor_settings.mMaxTorqueLimit = (float)twist_motor_max_torque;
+ }
+}
+
+void JoltConeTwistJoint3D::_limits_changed() {
+ rebuild();
+ _wake_up_bodies();
+}
+
+void JoltConeTwistJoint3D::_swing_motor_state_changed() {
+ _update_swing_motor_state();
+ _wake_up_bodies();
+}
+
+void JoltConeTwistJoint3D::_twist_motor_state_changed() {
+ _update_twist_motor_state();
+ _wake_up_bodies();
+}
+
+void JoltConeTwistJoint3D::_motor_velocity_changed() {
+ _update_motor_velocity();
+ _wake_up_bodies();
+}
+
+void JoltConeTwistJoint3D::_swing_motor_limit_changed() {
+ _update_swing_motor_limit();
+ _wake_up_bodies();
+}
+
+void JoltConeTwistJoint3D::_twist_motor_limit_changed() {
+ _update_twist_motor_limit();
+ _wake_up_bodies();
+}
+
+JoltConeTwistJoint3D::JoltConeTwistJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b) :
+ JoltJoint3D(p_old_joint, p_body_a, p_body_b, p_local_ref_a, p_local_ref_b) {
+ rebuild();
+}
+
+double JoltConeTwistJoint3D::get_param(PhysicsServer3D::ConeTwistJointParam p_param) const {
+ switch (p_param) {
+ case PhysicsServer3D::CONE_TWIST_JOINT_SWING_SPAN: {
+ return swing_limit_span;
+ }
+ case PhysicsServer3D::CONE_TWIST_JOINT_TWIST_SPAN: {
+ return twist_limit_span;
+ }
+ case PhysicsServer3D::CONE_TWIST_JOINT_BIAS: {
+ return DEFAULT_BIAS;
+ }
+ case PhysicsServer3D::CONE_TWIST_JOINT_SOFTNESS: {
+ return DEFAULT_SOFTNESS;
+ }
+ case PhysicsServer3D::CONE_TWIST_JOINT_RELAXATION: {
+ return DEFAULT_RELAXATION;
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled cone twist joint parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltConeTwistJoint3D::set_param(PhysicsServer3D::ConeTwistJointParam p_param, double p_value) {
+ switch (p_param) {
+ case PhysicsServer3D::CONE_TWIST_JOINT_SWING_SPAN: {
+ swing_limit_span = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::CONE_TWIST_JOINT_TWIST_SPAN: {
+ twist_limit_span = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::CONE_TWIST_JOINT_BIAS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_BIAS)) {
+ WARN_PRINT(vformat("Cone twist joint bias is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::CONE_TWIST_JOINT_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_SOFTNESS)) {
+ WARN_PRINT(vformat("Cone twist joint softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::CONE_TWIST_JOINT_RELAXATION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_RELAXATION)) {
+ WARN_PRINT(vformat("Cone twist joint relaxation is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled cone twist joint parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+double JoltConeTwistJoint3D::get_jolt_param(JoltParameter p_param) const {
+ switch (p_param) {
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_SWING_MOTOR_TARGET_VELOCITY_Y: {
+ return swing_motor_target_speed_y;
+ }
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_SWING_MOTOR_TARGET_VELOCITY_Z: {
+ return swing_motor_target_speed_z;
+ }
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_TWIST_MOTOR_TARGET_VELOCITY: {
+ return twist_motor_target_speed;
+ }
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_SWING_MOTOR_MAX_TORQUE: {
+ return swing_motor_max_torque;
+ }
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_TWIST_MOTOR_MAX_TORQUE: {
+ return twist_motor_max_torque;
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltConeTwistJoint3D::set_jolt_param(JoltParameter p_param, double p_value) {
+ switch (p_param) {
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_SWING_MOTOR_TARGET_VELOCITY_Y: {
+ swing_motor_target_speed_y = p_value;
+ _motor_velocity_changed();
+ } break;
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_SWING_MOTOR_TARGET_VELOCITY_Z: {
+ swing_motor_target_speed_z = p_value;
+ _motor_velocity_changed();
+ } break;
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_TWIST_MOTOR_TARGET_VELOCITY: {
+ twist_motor_target_speed = p_value;
+ _motor_velocity_changed();
+ } break;
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_SWING_MOTOR_MAX_TORQUE: {
+ swing_motor_max_torque = p_value;
+ _swing_motor_limit_changed();
+ } break;
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_TWIST_MOTOR_MAX_TORQUE: {
+ twist_motor_max_torque = p_value;
+ _twist_motor_limit_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+bool JoltConeTwistJoint3D::get_jolt_flag(JoltFlag p_flag) const {
+ switch (p_flag) {
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_USE_SWING_LIMIT: {
+ return swing_limit_enabled;
+ }
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_USE_TWIST_LIMIT: {
+ return twist_limit_enabled;
+ }
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_ENABLE_SWING_MOTOR: {
+ return swing_motor_enabled;
+ }
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_ENABLE_TWIST_MOTOR: {
+ return twist_motor_enabled;
+ }
+ default: {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ }
+ }
+}
+
+void JoltConeTwistJoint3D::set_jolt_flag(JoltFlag p_flag, bool p_enabled) {
+ switch (p_flag) {
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_USE_SWING_LIMIT: {
+ swing_limit_enabled = p_enabled;
+ _limits_changed();
+ } break;
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_USE_TWIST_LIMIT: {
+ twist_limit_enabled = p_enabled;
+ _limits_changed();
+ } break;
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_ENABLE_SWING_MOTOR: {
+ swing_motor_enabled = p_enabled;
+ _swing_motor_state_changed();
+ } break;
+ case JoltPhysicsServer3D::CONE_TWIST_JOINT_FLAG_ENABLE_TWIST_MOTOR: {
+ twist_motor_enabled = p_enabled;
+ _twist_motor_state_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ } break;
+ }
+}
+
+float JoltConeTwistJoint3D::get_applied_force() const {
+ JPH::SwingTwistConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ ERR_FAIL_NULL_V(constraint, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ return constraint->GetTotalLambdaPosition().Length() / last_step;
+}
+
+float JoltConeTwistJoint3D::get_applied_torque() const {
+ JPH::SwingTwistConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ ERR_FAIL_NULL_V(constraint, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ const JPH::Vec3 swing_twist_lambda = JPH::Vec3(constraint->GetTotalLambdaTwist(), constraint->GetTotalLambdaSwingY(), constraint->GetTotalLambdaSwingZ());
+
+ // Note that the motor lambda is in a different space than the swing twist lambda, and since the two forces can cancel each other it is
+ // technically incorrect to just add them. The bodies themselves have moved, so we can't transform one into the space of the other anymore.
+ const float total_lambda = swing_twist_lambda.Length() + constraint->GetTotalLambdaMotor().Length();
+
+ return total_lambda / last_step;
+}
+
+void JoltConeTwistJoint3D::rebuild() {
+ destroy();
+
+ JoltSpace3D *space = get_space();
+
+ if (space == nullptr) {
+ return;
+ }
+
+ const JPH::BodyID body_ids[2] = {
+ body_a != nullptr ? body_a->get_jolt_id() : JPH::BodyID(),
+ body_b != nullptr ? body_b->get_jolt_id() : JPH::BodyID()
+ };
+
+ const JoltWritableBodies3D jolt_bodies = space->write_bodies(body_ids, 2);
+
+ JPH::Body *jolt_body_a = static_cast(jolt_bodies[0]);
+ JPH::Body *jolt_body_b = static_cast(jolt_bodies[1]);
+
+ ERR_FAIL_COND(jolt_body_a == nullptr && jolt_body_b == nullptr);
+
+ Transform3D shifted_ref_a;
+ Transform3D shifted_ref_b;
+
+ _shift_reference_frames(Vector3(), Vector3(), shifted_ref_a, shifted_ref_b);
+
+ jolt_ref = _build_swing_twist(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b, (float)swing_limit_span, (float)twist_limit_span);
+
+ space->add_joint(this);
+
+ _update_enabled();
+ _update_iterations();
+ _update_swing_motor_state();
+ _update_twist_motor_state();
+ _update_motor_velocity();
+ _update_swing_motor_limit();
+ _update_twist_motor_limit();
+}
diff --git a/modules/jolt_physics/joints/jolt_cone_twist_joint_3d.h b/modules/jolt_physics/joints/jolt_cone_twist_joint_3d.h
new file mode 100644
index 000000000000..171e8457b088
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_cone_twist_joint_3d.h
@@ -0,0 +1,97 @@
+/**************************************************************************/
+/* jolt_cone_twist_joint_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CONE_TWIST_JOINT_3D_H
+#define JOLT_CONE_TWIST_JOINT_3D_H
+
+#include "../jolt_physics_server_3d.h"
+#include "jolt_joint_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Body/Body.h"
+
+class JoltConeTwistJoint3D final : public JoltJoint3D {
+ typedef PhysicsServer3D::ConeTwistJointParam Parameter;
+ typedef JoltPhysicsServer3D::ConeTwistJointParamJolt JoltParameter;
+ typedef JoltPhysicsServer3D::ConeTwistJointFlagJolt JoltFlag;
+
+ double swing_limit_span = 0.0;
+ double twist_limit_span = 0.0;
+
+ double swing_motor_target_speed_y = 0.0;
+ double swing_motor_target_speed_z = 0.0;
+ double twist_motor_target_speed = 0.0;
+
+ double swing_motor_max_torque = FLT_MAX;
+ double twist_motor_max_torque = FLT_MAX;
+
+ bool swing_limit_enabled = true;
+ bool twist_limit_enabled = true;
+
+ bool swing_motor_enabled = false;
+ bool twist_motor_enabled = false;
+
+ JPH::Constraint *_build_swing_twist(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b, float p_swing_limit_span, float p_twist_limit_span) const;
+
+ void _update_swing_motor_state();
+ void _update_twist_motor_state();
+ void _update_motor_velocity();
+ void _update_swing_motor_limit();
+ void _update_twist_motor_limit();
+
+ void _limits_changed();
+ void _swing_motor_state_changed();
+ void _twist_motor_state_changed();
+ void _motor_velocity_changed();
+ void _swing_motor_limit_changed();
+ void _twist_motor_limit_changed();
+
+public:
+ JoltConeTwistJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b);
+
+ virtual PhysicsServer3D::JointType get_type() const override { return PhysicsServer3D::JOINT_TYPE_CONE_TWIST; }
+
+ double get_param(PhysicsServer3D::ConeTwistJointParam p_param) const;
+ void set_param(PhysicsServer3D::ConeTwistJointParam p_param, double p_value);
+
+ double get_jolt_param(JoltParameter p_param) const;
+ void set_jolt_param(JoltParameter p_param, double p_value);
+
+ bool get_jolt_flag(JoltFlag p_flag) const;
+ void set_jolt_flag(JoltFlag p_flag, bool p_enabled);
+
+ float get_applied_force() const;
+ float get_applied_torque() const;
+
+ virtual void rebuild() override;
+};
+
+#endif // JOLT_CONE_TWIST_JOINT_3D_H
diff --git a/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.cpp b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.cpp
new file mode 100644
index 000000000000..0fa67f182b5b
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.cpp
@@ -0,0 +1,696 @@
+/**************************************************************************/
+/* jolt_generic_6dof_joint_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_generic_6dof_joint_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_body_3d.h"
+#include "../spaces/jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Constraints/SixDOFConstraint.h"
+
+namespace {
+
+constexpr double DEFAULT_LINEAR_LIMIT_SOFTNESS = 0.7;
+constexpr double DEFAULT_LINEAR_RESTITUTION = 0.5;
+constexpr double DEFAULT_LINEAR_DAMPING = 1.0;
+
+constexpr double DEFAULT_ANGULAR_LIMIT_SOFTNESS = 0.5;
+constexpr double DEFAULT_ANGULAR_DAMPING = 1.0;
+constexpr double DEFAULT_ANGULAR_RESTITUTION = 0.0;
+constexpr double DEFAULT_ANGULAR_FORCE_LIMIT = 0.0;
+constexpr double DEFAULT_ANGULAR_ERP = 0.5;
+
+} // namespace
+
+JPH::Constraint *JoltGeneric6DOFJoint3D::_build_6dof(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) const {
+ JPH::SixDOFConstraintSettings constraint_settings;
+
+ for (int axis = 0; axis < AXIS_COUNT; ++axis) {
+ double lower = limit_lower[axis];
+ double upper = limit_upper[axis];
+
+ if (axis >= AXIS_ANGULAR_X && axis <= AXIS_ANGULAR_Z) {
+ const double temp = lower;
+ lower = -upper;
+ upper = -temp;
+ }
+
+ if (!limit_enabled[axis] || lower > upper) {
+ constraint_settings.MakeFreeAxis((JoltAxis)axis);
+ } else {
+ constraint_settings.SetLimitedAxis((JoltAxis)axis, (float)lower, (float)upper);
+ }
+ }
+
+ constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
+ constraint_settings.mPosition1 = to_jolt_r(p_shifted_ref_a.origin);
+ constraint_settings.mAxisX1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mAxisY1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Y));
+ constraint_settings.mPosition2 = to_jolt_r(p_shifted_ref_b.origin);
+ constraint_settings.mAxisX2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mAxisY2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Y));
+ constraint_settings.mSwingType = JPH::ESwingType::Pyramid;
+
+ if (p_jolt_body_a == nullptr) {
+ return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);
+ } else if (p_jolt_body_b == nullptr) {
+ return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);
+ } else {
+ return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);
+ }
+}
+
+void JoltGeneric6DOFJoint3D::_update_limit_spring_parameters(int p_axis) {
+ JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ if (unlikely(constraint == nullptr)) {
+ return;
+ }
+
+ JPH::SpringSettings settings = constraint->GetLimitsSpringSettings((JoltAxis)p_axis);
+
+ settings.mMode = JPH::ESpringMode::FrequencyAndDamping;
+
+ if (limit_spring_enabled[p_axis]) {
+ settings.mFrequency = (float)limit_spring_frequency[p_axis];
+ settings.mDamping = (float)limit_spring_damping[p_axis];
+ } else {
+ settings.mFrequency = 0.0f;
+ settings.mDamping = 0.0f;
+ }
+
+ constraint->SetLimitsSpringSettings((JoltAxis)p_axis, settings);
+}
+
+void JoltGeneric6DOFJoint3D::_update_motor_state(int p_axis) {
+ if (JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ if (motor_enabled[p_axis]) {
+ constraint->SetMotorState((JoltAxis)p_axis, JPH::EMotorState::Velocity);
+ } else if (spring_enabled[p_axis]) {
+ constraint->SetMotorState((JoltAxis)p_axis, JPH::EMotorState::Position);
+ } else {
+ constraint->SetMotorState((JoltAxis)p_axis, JPH::EMotorState::Off);
+ }
+ }
+}
+
+void JoltGeneric6DOFJoint3D::_update_motor_velocity(int p_axis) {
+ JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ if (unlikely(constraint == nullptr)) {
+ return;
+ }
+
+ if (p_axis >= AXIS_LINEAR_X && p_axis <= AXIS_LINEAR_Z) {
+ constraint->SetTargetVelocityCS(JPH::Vec3(
+ (float)motor_speed[AXIS_LINEAR_X],
+ (float)motor_speed[AXIS_LINEAR_Y],
+ (float)motor_speed[AXIS_LINEAR_Z]));
+ } else {
+ // We flip the direction since Jolt is CCW but Godot is CW.
+ constraint->SetTargetAngularVelocityCS(JPH::Vec3(
+ (float)-motor_speed[AXIS_ANGULAR_X],
+ (float)-motor_speed[AXIS_ANGULAR_Y],
+ (float)-motor_speed[AXIS_ANGULAR_Z]));
+ }
+}
+
+void JoltGeneric6DOFJoint3D::_update_motor_limit(int p_axis) {
+ JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ if (unlikely(constraint == nullptr)) {
+ return;
+ }
+
+ JPH::MotorSettings &motor_settings = constraint->GetMotorSettings((JoltAxis)p_axis);
+
+ float limit = FLT_MAX;
+
+ if (motor_enabled[p_axis]) {
+ limit = (float)motor_limit[p_axis];
+ } else if (spring_enabled[p_axis]) {
+ limit = (float)spring_limit[p_axis];
+ }
+
+ if (p_axis >= AXIS_LINEAR_X && p_axis <= AXIS_LINEAR_Z) {
+ motor_settings.SetForceLimit(limit);
+ } else {
+ motor_settings.SetTorqueLimit(limit);
+ }
+}
+
+void JoltGeneric6DOFJoint3D::_update_spring_parameters(int p_axis) {
+ JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ if (unlikely(constraint == nullptr)) {
+ return;
+ }
+
+ JPH::MotorSettings &motor_settings = constraint->GetMotorSettings((JoltAxis)p_axis);
+
+ if (spring_use_frequency[p_axis]) {
+ motor_settings.mSpringSettings.mMode = JPH::ESpringMode::FrequencyAndDamping;
+ motor_settings.mSpringSettings.mFrequency = (float)spring_frequency[p_axis];
+ } else {
+ motor_settings.mSpringSettings.mMode = JPH::ESpringMode::StiffnessAndDamping;
+ motor_settings.mSpringSettings.mStiffness = (float)spring_stiffness[p_axis];
+ }
+
+ motor_settings.mSpringSettings.mDamping = (float)spring_damping[p_axis];
+}
+
+void JoltGeneric6DOFJoint3D::_update_spring_equilibrium(int p_axis) {
+ JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ if (unlikely(constraint == nullptr)) {
+ return;
+ }
+
+ if (p_axis >= AXIS_LINEAR_X && p_axis <= AXIS_LINEAR_Z) {
+ const Vector3 target_position = Vector3(
+ (float)spring_equilibrium[AXIS_LINEAR_X],
+ (float)spring_equilibrium[AXIS_LINEAR_Y],
+ (float)spring_equilibrium[AXIS_LINEAR_Z]);
+
+ constraint->SetTargetPositionCS(to_jolt(target_position));
+ } else {
+ // We flip the direction since Jolt is CCW but Godot is CW.
+ const Basis target_orientation = Basis::from_euler(
+ Vector3((float)-spring_equilibrium[AXIS_ANGULAR_X],
+ (float)-spring_equilibrium[AXIS_ANGULAR_Y],
+ (float)-spring_equilibrium[AXIS_ANGULAR_Z]),
+ EulerOrder::ZYX);
+
+ constraint->SetTargetOrientationCS(to_jolt(target_orientation));
+ }
+}
+
+void JoltGeneric6DOFJoint3D::_limits_changed() {
+ rebuild();
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_limit_spring_parameters_changed(int p_axis) {
+ _update_limit_spring_parameters(p_axis);
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_motor_state_changed(int p_axis) {
+ _update_motor_state(p_axis);
+ _update_motor_limit(p_axis);
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_motor_speed_changed(int p_axis) {
+ _update_motor_velocity(p_axis);
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_motor_limit_changed(int p_axis) {
+ _update_motor_limit(p_axis);
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_spring_state_changed(int p_axis) {
+ _update_motor_state(p_axis);
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_spring_parameters_changed(int p_axis) {
+ _update_spring_parameters(p_axis);
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_spring_equilibrium_changed(int p_axis) {
+ _update_spring_equilibrium(p_axis);
+ _wake_up_bodies();
+}
+
+void JoltGeneric6DOFJoint3D::_spring_limit_changed(int p_axis) {
+ _update_motor_limit(p_axis);
+ _wake_up_bodies();
+}
+
+JoltGeneric6DOFJoint3D::JoltGeneric6DOFJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b) :
+ JoltJoint3D(p_old_joint, p_body_a, p_body_b, p_local_ref_a, p_local_ref_b) {
+ rebuild();
+}
+
+double JoltGeneric6DOFJoint3D::get_param(Axis p_axis, Param p_param) const {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_param) {
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_LOWER_LIMIT: {
+ return limit_lower[axis_lin];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_UPPER_LIMIT: {
+ return limit_upper[axis_lin];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS: {
+ return DEFAULT_LINEAR_LIMIT_SOFTNESS;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_RESTITUTION: {
+ return DEFAULT_LINEAR_RESTITUTION;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_DAMPING: {
+ return DEFAULT_LINEAR_DAMPING;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: {
+ return motor_speed[axis_lin];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: {
+ return motor_limit[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS: {
+ return spring_stiffness[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_DAMPING: {
+ return spring_damping[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT: {
+ return spring_equilibrium[axis_lin];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: {
+ return limit_lower[axis_ang];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_UPPER_LIMIT: {
+ return limit_upper[axis_ang];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS: {
+ return DEFAULT_ANGULAR_LIMIT_SOFTNESS;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_DAMPING: {
+ return DEFAULT_ANGULAR_DAMPING;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_RESTITUTION: {
+ return DEFAULT_ANGULAR_RESTITUTION;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_FORCE_LIMIT: {
+ return DEFAULT_ANGULAR_FORCE_LIMIT;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_ERP: {
+ return DEFAULT_ANGULAR_ERP;
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY: {
+ return motor_speed[axis_ang];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT: {
+ return motor_limit[axis_ang];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS: {
+ return spring_stiffness[axis_ang];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_DAMPING: {
+ return spring_damping[axis_ang];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: {
+ return spring_equilibrium[axis_ang];
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltGeneric6DOFJoint3D::set_param(Axis p_axis, Param p_param, double p_value) {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_param) {
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_LOWER_LIMIT: {
+ limit_lower[axis_lin] = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_UPPER_LIMIT: {
+ limit_upper[axis_lin] = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_LIMIT_SOFTNESS)) {
+ WARN_PRINT(vformat("6DOF joint linear limit softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_RESTITUTION)) {
+ WARN_PRINT(vformat("6DOF joint linear restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_DAMPING)) {
+ WARN_PRINT(vformat("6DOF joint linear damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY: {
+ motor_speed[axis_lin] = p_value;
+ _motor_speed_changed(axis_lin);
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT: {
+ motor_limit[axis_lin] = p_value;
+ _motor_limit_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_STIFFNESS: {
+ spring_stiffness[axis_lin] = p_value;
+ _spring_parameters_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_DAMPING: {
+ spring_damping[axis_lin] = p_value;
+ _spring_parameters_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT: {
+ spring_equilibrium[axis_lin] = p_value;
+ _spring_equilibrium_changed(axis_lin);
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: {
+ limit_lower[axis_ang] = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_UPPER_LIMIT: {
+ limit_upper[axis_ang] = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_LIMIT_SOFTNESS)) {
+ WARN_PRINT(vformat("6DOF joint angular limit softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_DAMPING)) {
+ WARN_PRINT(vformat("6DOF joint angular damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_RESTITUTION)) {
+ WARN_PRINT(vformat("6DOF joint angular restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_FORCE_LIMIT: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_FORCE_LIMIT)) {
+ WARN_PRINT(vformat("6DOF joint angular force limit is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_ERP: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_ERP)) {
+ WARN_PRINT(vformat("6DOF joint angular ERP is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY: {
+ motor_speed[axis_ang] = p_value;
+ _motor_speed_changed(axis_ang);
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT: {
+ motor_limit[axis_ang] = p_value;
+ _motor_limit_changed(axis_ang);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS: {
+ spring_stiffness[axis_ang] = p_value;
+ _spring_parameters_changed(axis_ang);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_DAMPING: {
+ spring_damping[axis_ang] = p_value;
+ _spring_parameters_changed(axis_ang);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: {
+ spring_equilibrium[axis_ang] = p_value;
+ _spring_equilibrium_changed(axis_ang);
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+bool JoltGeneric6DOFJoint3D::get_flag(Axis p_axis, Flag p_flag) const {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_flag) {
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT: {
+ return limit_enabled[axis_lin];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT: {
+ return limit_enabled[axis_ang];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING: {
+ return spring_enabled[axis_ang];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING: {
+ return spring_enabled[axis_lin];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_MOTOR: {
+ return motor_enabled[axis_ang];
+ }
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: {
+ return motor_enabled[axis_lin];
+ }
+ default: {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ }
+ }
+}
+
+void JoltGeneric6DOFJoint3D::set_flag(Axis p_axis, Flag p_flag, bool p_enabled) {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_flag) {
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT: {
+ limit_enabled[axis_lin] = p_enabled;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT: {
+ limit_enabled[axis_ang] = p_enabled;
+ _limits_changed();
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING: {
+ spring_enabled[axis_ang] = p_enabled;
+ _spring_state_changed(axis_ang);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING: {
+ spring_enabled[axis_lin] = p_enabled;
+ _spring_state_changed(axis_lin);
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_MOTOR: {
+ motor_enabled[axis_ang] = p_enabled;
+ _motor_state_changed(axis_ang);
+ } break;
+ case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR: {
+ motor_enabled[axis_lin] = p_enabled;
+ _motor_state_changed(axis_lin);
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ } break;
+ }
+}
+
+double JoltGeneric6DOFJoint3D::get_jolt_param(Axis p_axis, JoltParam p_param) const {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_param) {
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_FREQUENCY: {
+ return spring_frequency[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_MAX_FORCE: {
+ return spring_limit[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SPRING_FREQUENCY: {
+ return limit_spring_frequency[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SPRING_DAMPING: {
+ return limit_spring_damping[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_FREQUENCY: {
+ return spring_frequency[axis_ang];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_MAX_TORQUE: {
+ return spring_limit[axis_ang];
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltGeneric6DOFJoint3D::set_jolt_param(Axis p_axis, JoltParam p_param, double p_value) {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_param) {
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_FREQUENCY: {
+ spring_frequency[axis_lin] = p_value;
+ _spring_parameters_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_MAX_FORCE: {
+ spring_limit[axis_lin] = p_value;
+ _spring_limit_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SPRING_FREQUENCY: {
+ limit_spring_frequency[axis_lin] = p_value;
+ _limit_spring_parameters_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SPRING_DAMPING: {
+ limit_spring_damping[axis_lin] = p_value;
+ _limit_spring_parameters_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_FREQUENCY: {
+ spring_frequency[axis_ang] = p_value;
+ _spring_parameters_changed(axis_ang);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_MAX_TORQUE: {
+ spring_limit[axis_ang] = p_value;
+ _spring_limit_changed(axis_ang);
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+bool JoltGeneric6DOFJoint3D::get_jolt_flag(Axis p_axis, JoltFlag p_flag) const {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_flag) {
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT_SPRING: {
+ return limit_spring_enabled[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING_FREQUENCY: {
+ return spring_use_frequency[axis_lin];
+ }
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING_FREQUENCY: {
+ return spring_use_frequency[axis_ang];
+ }
+ default: {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ }
+ }
+}
+
+void JoltGeneric6DOFJoint3D::set_jolt_flag(Axis p_axis, JoltFlag p_flag, bool p_enabled) {
+ const int axis_lin = AXES_LINEAR + (int)p_axis;
+ const int axis_ang = AXES_ANGULAR + (int)p_axis;
+
+ switch ((int)p_flag) {
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT_SPRING: {
+ limit_spring_enabled[axis_lin] = p_enabled;
+ _limit_spring_parameters_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING_FREQUENCY: {
+ spring_use_frequency[axis_lin] = p_enabled;
+ _spring_parameters_changed(axis_lin);
+ } break;
+ case JoltPhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING_FREQUENCY: {
+ spring_use_frequency[axis_ang] = p_enabled;
+ _spring_parameters_changed(axis_ang);
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ } break;
+ }
+}
+
+float JoltGeneric6DOFJoint3D::get_applied_force() const {
+ JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ ERR_FAIL_NULL_V(constraint, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ const JPH::Vec3 total_lambda = constraint->GetTotalLambdaPosition() + constraint->GetTotalLambdaMotorTranslation();
+
+ return total_lambda.Length() / last_step;
+}
+
+float JoltGeneric6DOFJoint3D::get_applied_torque() const {
+ JPH::SixDOFConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ ERR_FAIL_NULL_V(constraint, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ const JPH::Vec3 total_lambda = constraint->GetTotalLambdaRotation() + constraint->GetTotalLambdaMotorRotation();
+
+ return total_lambda.Length() / last_step;
+}
+
+void JoltGeneric6DOFJoint3D::rebuild() {
+ destroy();
+
+ JoltSpace3D *space = get_space();
+ if (space == nullptr) {
+ return;
+ }
+
+ const JPH::BodyID body_ids[2] = {
+ body_a != nullptr ? body_a->get_jolt_id() : JPH::BodyID(),
+ body_b != nullptr ? body_b->get_jolt_id() : JPH::BodyID()
+ };
+
+ const JoltWritableBodies3D jolt_bodies = space->write_bodies(body_ids, 2);
+
+ JPH::Body *jolt_body_a = static_cast(jolt_bodies[0]);
+ JPH::Body *jolt_body_b = static_cast(jolt_bodies[1]);
+
+ ERR_FAIL_COND(jolt_body_a == nullptr && jolt_body_b == nullptr);
+
+ Transform3D shifted_ref_a;
+ Transform3D shifted_ref_b;
+
+ _shift_reference_frames(Vector3(), Vector3(), shifted_ref_a, shifted_ref_b);
+
+ jolt_ref = _build_6dof(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b);
+
+ space->add_joint(this);
+
+ _update_enabled();
+ _update_iterations();
+
+ _update_limit_spring_parameters(AXIS_LINEAR_X);
+ _update_limit_spring_parameters(AXIS_LINEAR_Y);
+ _update_limit_spring_parameters(AXIS_LINEAR_Z);
+
+ for (int axis = 0; axis < AXIS_COUNT; ++axis) {
+ _update_motor_state(axis);
+ _update_motor_velocity(axis);
+ _update_motor_limit(axis);
+ _update_spring_parameters(axis);
+ _update_spring_equilibrium(axis);
+ }
+}
diff --git a/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.h b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.h
new file mode 100644
index 000000000000..9d7204897867
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.h
@@ -0,0 +1,127 @@
+/**************************************************************************/
+/* jolt_generic_6dof_joint_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_GENERIC_6DOF_JOINT_3D_H
+#define JOLT_GENERIC_6DOF_JOINT_3D_H
+
+#include "../jolt_physics_server_3d.h"
+#include "jolt_joint_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Constraints/SixDOFConstraint.h"
+
+class JoltGeneric6DOFJoint3D final : public JoltJoint3D {
+ typedef Vector3::Axis Axis;
+ typedef JPH::SixDOFConstraintSettings::EAxis JoltAxis;
+ typedef PhysicsServer3D::G6DOFJointAxisParam Param;
+ typedef JoltPhysicsServer3D::G6DOFJointAxisParamJolt JoltParam;
+ typedef PhysicsServer3D::G6DOFJointAxisFlag Flag;
+ typedef JoltPhysicsServer3D::G6DOFJointAxisFlagJolt JoltFlag;
+
+ enum {
+ AXIS_LINEAR_X = JoltAxis::TranslationX,
+ AXIS_LINEAR_Y = JoltAxis::TranslationY,
+ AXIS_LINEAR_Z = JoltAxis::TranslationZ,
+ AXIS_ANGULAR_X = JoltAxis::RotationX,
+ AXIS_ANGULAR_Y = JoltAxis::RotationY,
+ AXIS_ANGULAR_Z = JoltAxis::RotationZ,
+ AXIS_COUNT = JoltAxis::Num,
+ AXES_LINEAR = AXIS_LINEAR_X,
+ AXES_ANGULAR = AXIS_ANGULAR_X,
+ };
+
+ double limit_lower[AXIS_COUNT] = {};
+ double limit_upper[AXIS_COUNT] = {};
+
+ double limit_spring_frequency[AXIS_COUNT] = {};
+ double limit_spring_damping[AXIS_COUNT] = {};
+
+ double motor_speed[AXIS_COUNT] = {};
+ double motor_limit[AXIS_COUNT] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+ double spring_stiffness[AXIS_COUNT] = {};
+ double spring_frequency[AXIS_COUNT] = {};
+ double spring_damping[AXIS_COUNT] = {};
+ double spring_equilibrium[AXIS_COUNT] = {};
+ double spring_limit[AXIS_COUNT] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+ bool limit_enabled[AXIS_COUNT] = {};
+
+ bool limit_spring_enabled[AXIS_COUNT] = {};
+
+ bool motor_enabled[AXIS_COUNT] = {};
+
+ bool spring_enabled[AXIS_COUNT] = {};
+ bool spring_use_frequency[AXIS_COUNT] = {};
+
+ JPH::Constraint *_build_6dof(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) const;
+
+ void _update_limit_spring_parameters(int p_axis);
+ void _update_motor_state(int p_axis);
+ void _update_motor_velocity(int p_axis);
+ void _update_motor_limit(int p_axis);
+ void _update_spring_parameters(int p_axis);
+ void _update_spring_equilibrium(int p_axis);
+
+ void _limits_changed();
+ void _limit_spring_parameters_changed(int p_axis);
+ void _motor_state_changed(int p_axis);
+ void _motor_speed_changed(int p_axis);
+ void _motor_limit_changed(int p_axis);
+ void _spring_state_changed(int p_axis);
+ void _spring_parameters_changed(int p_axis);
+ void _spring_equilibrium_changed(int p_axis);
+ void _spring_limit_changed(int p_axis);
+
+public:
+ JoltGeneric6DOFJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b);
+
+ virtual PhysicsServer3D::JointType get_type() const override { return PhysicsServer3D::JOINT_TYPE_6DOF; }
+
+ double get_param(Axis p_axis, Param p_param) const;
+ void set_param(Axis p_axis, Param p_param, double p_value);
+
+ bool get_flag(Axis p_axis, Flag p_flag) const;
+ void set_flag(Axis p_axis, Flag p_flag, bool p_enabled);
+
+ double get_jolt_param(Axis p_axis, JoltParam p_param) const;
+ void set_jolt_param(Axis p_axis, JoltParam p_param, double p_value);
+
+ bool get_jolt_flag(Axis p_axis, JoltFlag p_flag) const;
+ void set_jolt_flag(Axis p_axis, JoltFlag p_flag, bool p_enabled);
+
+ float get_applied_force() const;
+ float get_applied_torque() const;
+
+ virtual void rebuild() override;
+};
+
+#endif // JOLT_GENERIC_6DOF_JOINT_3D_H
diff --git a/modules/jolt_physics/joints/jolt_hinge_joint_3d.cpp b/modules/jolt_physics/joints/jolt_hinge_joint_3d.cpp
new file mode 100644
index 000000000000..ba9360b559e5
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_hinge_joint_3d.cpp
@@ -0,0 +1,430 @@
+/**************************************************************************/
+/* jolt_hinge_joint_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_hinge_joint_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_body_3d.h"
+#include "../spaces/jolt_space_3d.h"
+
+#include "core/config/engine.h"
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Constraints/FixedConstraint.h"
+#include "Jolt/Physics/Constraints/HingeConstraint.h"
+
+namespace {
+
+constexpr double DEFAULT_BIAS = 0.3;
+constexpr double DEFAULT_LIMIT_BIAS = 0.3;
+constexpr double DEFAULT_SOFTNESS = 0.9;
+constexpr double DEFAULT_RELAXATION = 1.0;
+
+double estimate_physics_step() {
+ Engine *engine = Engine::get_singleton();
+
+ const double step = 1.0 / engine->get_physics_ticks_per_second();
+ const double step_scaled = step * engine->get_time_scale();
+
+ return step_scaled;
+}
+
+} // namespace
+
+JPH::Constraint *JoltHingeJoint3D::_build_hinge(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b, float p_limit) const {
+ JPH::HingeConstraintSettings constraint_settings;
+
+ constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
+ constraint_settings.mPoint1 = to_jolt_r(p_shifted_ref_a.origin);
+ constraint_settings.mHingeAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Z));
+ constraint_settings.mNormalAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mPoint2 = to_jolt_r(p_shifted_ref_b.origin);
+ constraint_settings.mHingeAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Z));
+ constraint_settings.mNormalAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mLimitsMin = -p_limit;
+ constraint_settings.mLimitsMax = p_limit;
+
+ if (limit_spring_enabled) {
+ constraint_settings.mLimitsSpringSettings.mFrequency = (float)limit_spring_frequency;
+ constraint_settings.mLimitsSpringSettings.mDamping = (float)limit_spring_damping;
+ }
+
+ if (p_jolt_body_a == nullptr) {
+ return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);
+ } else if (p_jolt_body_b == nullptr) {
+ return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);
+ } else {
+ return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);
+ }
+}
+
+JPH::Constraint *JoltHingeJoint3D::_build_fixed(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) const {
+ JPH::FixedConstraintSettings constraint_settings;
+
+ constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
+ constraint_settings.mAutoDetectPoint = false;
+ constraint_settings.mPoint1 = to_jolt_r(p_shifted_ref_a.origin);
+ constraint_settings.mAxisX1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mAxisY1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Y));
+ constraint_settings.mPoint2 = to_jolt_r(p_shifted_ref_b.origin);
+ constraint_settings.mAxisX2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mAxisY2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Y));
+
+ if (p_jolt_body_a == nullptr) {
+ return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);
+ } else if (p_jolt_body_b == nullptr) {
+ return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);
+ } else {
+ return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);
+ }
+}
+
+void JoltHingeJoint3D::_update_motor_state() {
+ if (unlikely(_is_fixed())) {
+ return;
+ }
+
+ if (JPH::HingeConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ constraint->SetMotorState(motor_enabled ? JPH::EMotorState::Velocity : JPH::EMotorState::Off);
+ }
+}
+
+void JoltHingeJoint3D::_update_motor_velocity() {
+ if (unlikely(_is_fixed())) {
+ return;
+ }
+
+ if (JPH::HingeConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ // We flip the direction since Jolt is CCW but Godot is CW.
+ constraint->SetTargetAngularVelocity((float)-motor_target_speed);
+ }
+}
+
+void JoltHingeJoint3D::_update_motor_limit() {
+ if (unlikely(_is_fixed())) {
+ return;
+ }
+
+ if (JPH::HingeConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ JPH::MotorSettings &motor_settings = constraint->GetMotorSettings();
+ motor_settings.mMinTorqueLimit = (float)-motor_max_torque;
+ motor_settings.mMaxTorqueLimit = (float)motor_max_torque;
+ }
+}
+
+void JoltHingeJoint3D::_limits_changed() {
+ rebuild();
+ _wake_up_bodies();
+}
+
+void JoltHingeJoint3D::_limit_spring_changed() {
+ rebuild();
+ _wake_up_bodies();
+}
+
+void JoltHingeJoint3D::_motor_state_changed() {
+ _update_motor_state();
+ _wake_up_bodies();
+}
+
+void JoltHingeJoint3D::_motor_speed_changed() {
+ _update_motor_velocity();
+ _wake_up_bodies();
+}
+
+void JoltHingeJoint3D::_motor_limit_changed() {
+ _update_motor_limit();
+ _wake_up_bodies();
+}
+
+JoltHingeJoint3D::JoltHingeJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b) :
+ JoltJoint3D(p_old_joint, p_body_a, p_body_b, p_local_ref_a, p_local_ref_b) {
+ rebuild();
+}
+
+double JoltHingeJoint3D::get_param(Parameter p_param) const {
+ switch (p_param) {
+ case PhysicsServer3D::HINGE_JOINT_BIAS: {
+ return DEFAULT_BIAS;
+ }
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER: {
+ return limit_upper;
+ }
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER: {
+ return limit_lower;
+ }
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS: {
+ return DEFAULT_LIMIT_BIAS;
+ }
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS: {
+ return DEFAULT_SOFTNESS;
+ }
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION: {
+ return DEFAULT_RELAXATION;
+ }
+ case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY: {
+ return motor_target_speed;
+ }
+ case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE: {
+ // With Godot using max impulse instead of max torque we don't have much choice but to calculate this and hope the timestep doesn't change.
+ return motor_max_torque * estimate_physics_step();
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltHingeJoint3D::set_param(Parameter p_param, double p_value) {
+ switch (p_param) {
+ case PhysicsServer3D::HINGE_JOINT_BIAS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_BIAS)) {
+ WARN_PRINT(vformat("Hinge joint bias is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER: {
+ limit_upper = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER: {
+ limit_lower = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LIMIT_BIAS)) {
+ WARN_PRINT(vformat("Hinge joint bias limit is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_SOFTNESS)) {
+ WARN_PRINT(vformat("Hinge joint softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_RELAXATION)) {
+ WARN_PRINT(vformat("Hinge joint relaxation is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY: {
+ motor_target_speed = p_value;
+ _motor_speed_changed();
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE: {
+ // With Godot using max impulse instead of max torque we don't have much choice but to calculate this and hope the timestep doesn't change.
+ motor_max_torque = p_value / estimate_physics_step();
+ _motor_limit_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+double JoltHingeJoint3D::get_jolt_param(JoltParameter p_param) const {
+ switch (p_param) {
+ case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_FREQUENCY: {
+ return limit_spring_frequency;
+ }
+ case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_DAMPING: {
+ return limit_spring_damping;
+ }
+ case JoltPhysicsServer3D::HINGE_JOINT_MOTOR_MAX_TORQUE: {
+ return motor_max_torque;
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltHingeJoint3D::set_jolt_param(JoltParameter p_param, double p_value) {
+ switch (p_param) {
+ case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_FREQUENCY: {
+ limit_spring_frequency = p_value;
+ _limit_spring_changed();
+ } break;
+ case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_DAMPING: {
+ limit_spring_damping = p_value;
+ _limit_spring_changed();
+ } break;
+ case JoltPhysicsServer3D::HINGE_JOINT_MOTOR_MAX_TORQUE: {
+ motor_max_torque = p_value;
+ _motor_limit_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+bool JoltHingeJoint3D::get_flag(Flag p_flag) const {
+ switch (p_flag) {
+ case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT: {
+ return limits_enabled;
+ }
+ case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR: {
+ return motor_enabled;
+ }
+ default: {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ }
+ }
+}
+
+void JoltHingeJoint3D::set_flag(Flag p_flag, bool p_enabled) {
+ switch (p_flag) {
+ case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT: {
+ limits_enabled = p_enabled;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR: {
+ motor_enabled = p_enabled;
+ _motor_state_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ } break;
+ }
+}
+
+bool JoltHingeJoint3D::get_jolt_flag(JoltFlag p_flag) const {
+ switch (p_flag) {
+ case JoltPhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT_SPRING: {
+ return limit_spring_enabled;
+ }
+ default: {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ }
+ }
+}
+
+void JoltHingeJoint3D::set_jolt_flag(JoltFlag p_flag, bool p_enabled) {
+ switch (p_flag) {
+ case JoltPhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT_SPRING: {
+ limit_spring_enabled = p_enabled;
+ _limit_spring_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ } break;
+ }
+}
+
+float JoltHingeJoint3D::get_applied_force() const {
+ ERR_FAIL_NULL_V(jolt_ref, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ if (_is_fixed()) {
+ JPH::FixedConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ return constraint->GetTotalLambdaPosition().Length() / last_step;
+ } else {
+ JPH::HingeConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ const JPH::Vec3 total_lambda = JPH::Vec3(constraint->GetTotalLambdaRotation()[0], constraint->GetTotalLambdaRotation()[1], constraint->GetTotalLambdaRotationLimits() + constraint->GetTotalLambdaMotor());
+ return total_lambda.Length() / last_step;
+ }
+}
+
+float JoltHingeJoint3D::get_applied_torque() const {
+ ERR_FAIL_NULL_V(jolt_ref, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ if (_is_fixed()) {
+ JPH::FixedConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ return constraint->GetTotalLambdaRotation().Length() / last_step;
+ } else {
+ JPH::HingeConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ return constraint->GetTotalLambdaRotation().Length() / last_step;
+ }
+}
+
+void JoltHingeJoint3D::rebuild() {
+ destroy();
+
+ JoltSpace3D *space = get_space();
+
+ if (space == nullptr) {
+ return;
+ }
+
+ const JPH::BodyID body_ids[2] = {
+ body_a != nullptr ? body_a->get_jolt_id() : JPH::BodyID(),
+ body_b != nullptr ? body_b->get_jolt_id() : JPH::BodyID()
+ };
+
+ const JoltWritableBodies3D jolt_bodies = space->write_bodies(body_ids, 2);
+
+ JPH::Body *jolt_body_a = static_cast(jolt_bodies[0]);
+ JPH::Body *jolt_body_b = static_cast(jolt_bodies[1]);
+
+ ERR_FAIL_COND(jolt_body_a == nullptr && jolt_body_b == nullptr);
+
+ float ref_shift = 0.0f;
+ float limit = JPH::JPH_PI;
+
+ if (limits_enabled && limit_lower <= limit_upper) {
+ const double limit_midpoint = (limit_lower + limit_upper) / 2.0f;
+
+ ref_shift = float(-limit_midpoint);
+ limit = float(limit_upper - limit_midpoint);
+ }
+
+ Transform3D shifted_ref_a;
+ Transform3D shifted_ref_b;
+
+ _shift_reference_frames(Vector3(), Vector3(0.0f, 0.0f, ref_shift), shifted_ref_a, shifted_ref_b);
+
+ if (_is_fixed()) {
+ jolt_ref = _build_fixed(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b);
+ } else {
+ jolt_ref = _build_hinge(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b, limit);
+ }
+
+ space->add_joint(this);
+
+ _update_enabled();
+ _update_iterations();
+ _update_motor_state();
+ _update_motor_velocity();
+ _update_motor_limit();
+}
diff --git a/modules/jolt_physics/joints/jolt_hinge_joint_3d.h b/modules/jolt_physics/joints/jolt_hinge_joint_3d.h
new file mode 100644
index 000000000000..05ac9c3ee7a3
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_hinge_joint_3d.h
@@ -0,0 +1,100 @@
+/**************************************************************************/
+/* jolt_hinge_joint_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_HINGE_JOINT_3D_H
+#define JOLT_HINGE_JOINT_3D_H
+
+#include "../jolt_physics_server_3d.h"
+#include "jolt_joint_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Constraints/SliderConstraint.h"
+
+class JoltHingeJoint3D final : public JoltJoint3D {
+ typedef PhysicsServer3D::HingeJointParam Parameter;
+ typedef JoltPhysicsServer3D::HingeJointParamJolt JoltParameter;
+ typedef PhysicsServer3D::HingeJointFlag Flag;
+ typedef JoltPhysicsServer3D::HingeJointFlagJolt JoltFlag;
+
+ double limit_lower = 0.0;
+ double limit_upper = 0.0;
+
+ double limit_spring_frequency = 0.0;
+ double limit_spring_damping = 0.0;
+
+ double motor_target_speed = 0.0f;
+ double motor_max_torque = FLT_MAX;
+
+ bool limits_enabled = false;
+ bool limit_spring_enabled = false;
+
+ bool motor_enabled = false;
+
+ JPH::Constraint *_build_hinge(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b, float p_limit) const;
+ JPH::Constraint *_build_fixed(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) const;
+
+ bool _is_sprung() const { return limit_spring_enabled && limit_spring_frequency > 0.0; }
+ bool _is_fixed() const { return limits_enabled && limit_lower == limit_upper && !_is_sprung(); }
+
+ void _update_motor_state();
+ void _update_motor_velocity();
+ void _update_motor_limit();
+
+ void _limits_changed();
+ void _limit_spring_changed();
+ void _motor_state_changed();
+ void _motor_speed_changed();
+ void _motor_limit_changed();
+
+public:
+ JoltHingeJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b);
+
+ virtual PhysicsServer3D::JointType get_type() const override { return PhysicsServer3D::JOINT_TYPE_HINGE; }
+
+ double get_param(Parameter p_param) const;
+ void set_param(Parameter p_param, double p_value);
+
+ double get_jolt_param(JoltParameter p_param) const;
+ void set_jolt_param(JoltParameter p_param, double p_value);
+
+ bool get_flag(Flag p_flag) const;
+ void set_flag(Flag p_flag, bool p_enabled);
+
+ bool get_jolt_flag(JoltFlag p_flag) const;
+ void set_jolt_flag(JoltFlag p_flag, bool p_enabled);
+
+ float get_applied_force() const;
+ float get_applied_torque() const;
+
+ virtual void rebuild() override;
+};
+
+#endif // JOLT_HINGE_JOINT_3D_H
diff --git a/modules/jolt_physics/joints/jolt_joint_3d.cpp b/modules/jolt_physics/joints/jolt_joint_3d.cpp
new file mode 100644
index 000000000000..b72ed5635d28
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_joint_3d.cpp
@@ -0,0 +1,237 @@
+/**************************************************************************/
+/* jolt_joint_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_joint_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_body_3d.h"
+#include "../spaces/jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+namespace {
+
+constexpr int DEFAULT_SOLVER_PRIORITY = 1;
+
+} // namespace
+
+void JoltJoint3D::_shift_reference_frames(const Vector3 &p_linear_shift, const Vector3 &p_angular_shift, Transform3D &r_shifted_ref_a, Transform3D &r_shifted_ref_b) {
+ Vector3 origin_a = local_ref_a.origin;
+ Vector3 origin_b = local_ref_b.origin;
+
+ if (body_a != nullptr) {
+ origin_a *= body_a->get_scale();
+ origin_a -= to_godot(body_a->get_jolt_shape()->GetCenterOfMass());
+ }
+
+ if (body_b != nullptr) {
+ origin_b *= body_b->get_scale();
+ origin_b -= to_godot(body_b->get_jolt_shape()->GetCenterOfMass());
+ }
+
+ const Basis &basis_a = local_ref_a.basis;
+ const Basis &basis_b = local_ref_b.basis;
+
+ const Basis shifted_basis_a = basis_a * Basis::from_euler(p_angular_shift, EulerOrder::ZYX);
+ const Vector3 shifted_origin_a = origin_a - basis_a.xform(p_linear_shift);
+
+ r_shifted_ref_a = Transform3D(shifted_basis_a, shifted_origin_a);
+ r_shifted_ref_b = Transform3D(basis_b, origin_b);
+}
+
+void JoltJoint3D::_wake_up_bodies() {
+ if (body_a != nullptr) {
+ body_a->wake_up();
+ }
+
+ if (body_b != nullptr) {
+ body_b->wake_up();
+ }
+}
+
+void JoltJoint3D::_update_enabled() {
+ if (jolt_ref != nullptr) {
+ jolt_ref->SetEnabled(enabled);
+ }
+}
+
+void JoltJoint3D::_update_iterations() {
+ if (jolt_ref != nullptr) {
+ jolt_ref->SetNumVelocityStepsOverride((JPH::uint)velocity_iterations);
+ jolt_ref->SetNumPositionStepsOverride((JPH::uint)position_iterations);
+ }
+}
+
+void JoltJoint3D::_enabled_changed() {
+ _update_enabled();
+ _wake_up_bodies();
+}
+
+void JoltJoint3D::_iterations_changed() {
+ _update_iterations();
+ _wake_up_bodies();
+}
+
+String JoltJoint3D::_bodies_to_string() const {
+ return vformat("'%s' and '%s'", body_a != nullptr ? body_a->to_string() : "", body_b != nullptr ? body_b->to_string() : "");
+}
+
+JoltJoint3D::JoltJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b) :
+ enabled(p_old_joint.enabled),
+ collision_disabled(p_old_joint.collision_disabled),
+ body_a(p_body_a),
+ body_b(p_body_b),
+ rid(p_old_joint.rid),
+ local_ref_a(p_local_ref_a),
+ local_ref_b(p_local_ref_b) {
+ if (body_a != nullptr) {
+ body_a->add_joint(this);
+ }
+
+ if (body_b != nullptr) {
+ body_b->add_joint(this);
+ }
+
+ if (body_b == nullptr && JoltProjectSettings::use_joint_world_node_a()) {
+ // The joint scene nodes will, when omitting one of the two body nodes, always pass in a
+ // null `body_b` to indicate it being the "world node", regardless of which body node you
+ // leave blank. So we need to correct for that if we wish to use the arguably more intuitive
+ // alternative where `body_a` is the "world node" instead.
+
+ SWAP(body_a, body_b);
+ SWAP(local_ref_a, local_ref_b);
+ }
+}
+
+JoltJoint3D::~JoltJoint3D() {
+ if (body_a != nullptr) {
+ body_a->remove_joint(this);
+ }
+
+ if (body_b != nullptr) {
+ body_b->remove_joint(this);
+ }
+
+ destroy();
+}
+
+JoltSpace3D *JoltJoint3D::get_space() const {
+ if (body_a != nullptr && body_b != nullptr) {
+ JoltSpace3D *space_a = body_a->get_space();
+ JoltSpace3D *space_b = body_b->get_space();
+
+ if (space_a == nullptr || space_b == nullptr) {
+ return nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(space_a != space_b, nullptr, vformat("Joint was found to connect bodies in different physics spaces. This joint will effectively be disabled. This joint connects %s.", _bodies_to_string()));
+
+ return space_a;
+ } else if (body_a != nullptr) {
+ return body_a->get_space();
+ } else if (body_b != nullptr) {
+ return body_b->get_space();
+ }
+
+ return nullptr;
+}
+
+void JoltJoint3D::set_enabled(bool p_enabled) {
+ if (enabled == p_enabled) {
+ return;
+ }
+
+ enabled = p_enabled;
+
+ _enabled_changed();
+}
+
+int JoltJoint3D::get_solver_priority() const {
+ return DEFAULT_SOLVER_PRIORITY;
+}
+
+void JoltJoint3D::set_solver_priority(int p_priority) {
+ if (p_priority != DEFAULT_SOLVER_PRIORITY) {
+ WARN_PRINT(vformat("Joint solver priority is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+}
+
+void JoltJoint3D::set_solver_velocity_iterations(int p_iterations) {
+ if (velocity_iterations == p_iterations) {
+ return;
+ }
+
+ velocity_iterations = p_iterations;
+
+ _iterations_changed();
+}
+
+void JoltJoint3D::set_solver_position_iterations(int p_iterations) {
+ if (position_iterations == p_iterations) {
+ return;
+ }
+
+ position_iterations = p_iterations;
+
+ _iterations_changed();
+}
+
+void JoltJoint3D::set_collision_disabled(bool p_disabled) {
+ collision_disabled = p_disabled;
+
+ if (body_a == nullptr || body_b == nullptr) {
+ return;
+ }
+
+ PhysicsServer3D *physics_server = PhysicsServer3D::get_singleton();
+
+ if (collision_disabled) {
+ physics_server->body_add_collision_exception(body_a->get_rid(), body_b->get_rid());
+ physics_server->body_add_collision_exception(body_b->get_rid(), body_a->get_rid());
+ } else {
+ physics_server->body_remove_collision_exception(body_a->get_rid(), body_b->get_rid());
+ physics_server->body_remove_collision_exception(body_b->get_rid(), body_a->get_rid());
+ }
+}
+
+void JoltJoint3D::destroy() {
+ if (jolt_ref == nullptr) {
+ return;
+ }
+
+ JoltSpace3D *space = get_space();
+
+ if (space != nullptr) {
+ space->remove_joint(this);
+ }
+
+ jolt_ref = nullptr;
+}
diff --git a/modules/jolt_physics/joints/jolt_joint_3d.h b/modules/jolt_physics/joints/jolt_joint_3d.h
new file mode 100644
index 000000000000..b3f39630afa5
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_joint_3d.h
@@ -0,0 +1,110 @@
+/**************************************************************************/
+/* jolt_joint_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_JOINT_3D_H
+#define JOLT_JOINT_3D_H
+
+#include "core/math/transform_3d.h"
+#include "core/string/ustring.h"
+#include "core/templates/rid.h"
+#include "servers/physics_server_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Constraints/Constraint.h"
+
+class JoltBody3D;
+class JoltSpace3D;
+
+class JoltJoint3D {
+protected:
+ bool enabled = true;
+ bool collision_disabled = false;
+
+ int velocity_iterations = 0;
+ int position_iterations = 0;
+
+ JPH::Ref jolt_ref;
+
+ JoltBody3D *body_a = nullptr;
+ JoltBody3D *body_b = nullptr;
+
+ RID rid;
+
+ Transform3D local_ref_a;
+ Transform3D local_ref_b;
+
+ void _shift_reference_frames(const Vector3 &p_linear_shift, const Vector3 &p_angular_shift, Transform3D &r_shifted_ref_a, Transform3D &r_shifted_ref_b);
+
+ void _wake_up_bodies();
+
+ void _update_enabled();
+ void _update_iterations();
+
+ void _enabled_changed();
+ void _iterations_changed();
+
+ String _bodies_to_string() const;
+
+public:
+ JoltJoint3D() = default;
+ JoltJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b);
+ virtual ~JoltJoint3D();
+
+ virtual PhysicsServer3D::JointType get_type() const { return PhysicsServer3D::JOINT_TYPE_MAX; }
+
+ RID get_rid() const { return rid; }
+ void set_rid(const RID &p_rid) { rid = p_rid; }
+
+ JoltSpace3D *get_space() const;
+
+ JPH::Constraint *get_jolt_ref() const { return jolt_ref; }
+
+ bool is_enabled() const { return enabled; }
+ void set_enabled(bool p_enabled);
+
+ int get_solver_priority() const;
+ void set_solver_priority(int p_priority);
+
+ int get_solver_velocity_iterations() const { return velocity_iterations; }
+ void set_solver_velocity_iterations(int p_iterations);
+
+ int get_solver_position_iterations() const { return position_iterations; }
+ void set_solver_position_iterations(int p_iterations);
+
+ bool is_collision_disabled() const { return collision_disabled; }
+ void set_collision_disabled(bool p_disabled);
+
+ void destroy();
+
+ virtual void rebuild() {}
+};
+
+#endif // JOLT_JOINT_3D_H
diff --git a/modules/jolt_physics/joints/jolt_pin_joint_3d.cpp b/modules/jolt_physics/joints/jolt_pin_joint_3d.cpp
new file mode 100644
index 000000000000..14aee380ce33
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_pin_joint_3d.cpp
@@ -0,0 +1,171 @@
+/**************************************************************************/
+/* jolt_pin_joint_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_pin_joint_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_body_3d.h"
+#include "../spaces/jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Constraints/PointConstraint.h"
+
+namespace {
+
+constexpr double DEFAULT_BIAS = 0.3;
+constexpr double DEFAULT_DAMPING = 1.0;
+constexpr double DEFAULT_IMPULSE_CLAMP = 0.0;
+
+} // namespace
+
+JPH::Constraint *JoltPinJoint3D::_build_pin(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) {
+ JPH::PointConstraintSettings constraint_settings;
+ constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
+ constraint_settings.mPoint1 = to_jolt_r(p_shifted_ref_a.origin);
+ constraint_settings.mPoint2 = to_jolt_r(p_shifted_ref_b.origin);
+
+ if (p_jolt_body_a == nullptr) {
+ return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);
+ } else if (p_jolt_body_b == nullptr) {
+ return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);
+ } else {
+ return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);
+ }
+}
+
+void JoltPinJoint3D::_points_changed() {
+ rebuild();
+ _wake_up_bodies();
+}
+
+JoltPinJoint3D::JoltPinJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Vector3 &p_local_a, const Vector3 &p_local_b) :
+ JoltJoint3D(p_old_joint, p_body_a, p_body_b, Transform3D({}, p_local_a), Transform3D({}, p_local_b)) {
+ rebuild();
+}
+
+void JoltPinJoint3D::set_local_a(const Vector3 &p_local_a) {
+ local_ref_a = Transform3D({}, p_local_a);
+ _points_changed();
+}
+
+void JoltPinJoint3D::set_local_b(const Vector3 &p_local_b) {
+ local_ref_b = Transform3D({}, p_local_b);
+ _points_changed();
+}
+
+double JoltPinJoint3D::get_param(PhysicsServer3D::PinJointParam p_param) const {
+ switch (p_param) {
+ case PhysicsServer3D::PIN_JOINT_BIAS: {
+ return DEFAULT_BIAS;
+ }
+ case PhysicsServer3D::PIN_JOINT_DAMPING: {
+ return DEFAULT_DAMPING;
+ }
+ case PhysicsServer3D::PIN_JOINT_IMPULSE_CLAMP: {
+ return DEFAULT_IMPULSE_CLAMP;
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled pin joint parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltPinJoint3D::set_param(PhysicsServer3D::PinJointParam p_param, double p_value) {
+ switch (p_param) {
+ case PhysicsServer3D::PIN_JOINT_BIAS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_BIAS)) {
+ WARN_PRINT(vformat("Pin joint bias is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::PIN_JOINT_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_DAMPING)) {
+ WARN_PRINT(vformat("Pin joint damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::PIN_JOINT_IMPULSE_CLAMP: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_IMPULSE_CLAMP)) {
+ WARN_PRINT(vformat("Pin joint impulse clamp is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled pin joint parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+float JoltPinJoint3D::get_applied_force() const {
+ JPH::PointConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ ERR_FAIL_NULL_V(constraint, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ return constraint->GetTotalLambdaPosition().Length() / last_step;
+}
+
+void JoltPinJoint3D::rebuild() {
+ destroy();
+
+ JoltSpace3D *space = get_space();
+
+ if (space == nullptr) {
+ return;
+ }
+
+ const JPH::BodyID body_ids[2] = {
+ body_a != nullptr ? body_a->get_jolt_id() : JPH::BodyID(),
+ body_b != nullptr ? body_b->get_jolt_id() : JPH::BodyID()
+ };
+
+ const JoltWritableBodies3D jolt_bodies = space->write_bodies(body_ids, 2);
+
+ JPH::Body *jolt_body_a = static_cast(jolt_bodies[0]);
+ JPH::Body *jolt_body_b = static_cast(jolt_bodies[1]);
+
+ ERR_FAIL_COND(jolt_body_a == nullptr && jolt_body_b == nullptr);
+
+ Transform3D shifted_ref_a;
+ Transform3D shifted_ref_b;
+
+ _shift_reference_frames(Vector3(), Vector3(), shifted_ref_a, shifted_ref_b);
+
+ jolt_ref = _build_pin(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b);
+
+ space->add_joint(this);
+
+ _update_enabled();
+ _update_iterations();
+}
diff --git a/modules/jolt_physics/joints/jolt_pin_joint_3d.h b/modules/jolt_physics/joints/jolt_pin_joint_3d.h
new file mode 100644
index 000000000000..55cee1ad2d47
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_pin_joint_3d.h
@@ -0,0 +1,64 @@
+/**************************************************************************/
+/* jolt_pin_joint_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_PIN_JOINT_3D_H
+#define JOLT_PIN_JOINT_3D_H
+
+#include "jolt_joint_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Constraints/SliderConstraint.h"
+
+class JoltPinJoint3D final : public JoltJoint3D {
+ static JPH::Constraint *_build_pin(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b);
+
+ void _points_changed();
+
+public:
+ JoltPinJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Vector3 &p_local_a, const Vector3 &p_local_b);
+
+ virtual PhysicsServer3D::JointType get_type() const override { return PhysicsServer3D::JOINT_TYPE_PIN; }
+
+ Vector3 get_local_a() const { return local_ref_a.origin; }
+ void set_local_a(const Vector3 &p_local_a);
+
+ Vector3 get_local_b() const { return local_ref_b.origin; }
+ void set_local_b(const Vector3 &p_local_b);
+
+ double get_param(PhysicsServer3D::PinJointParam p_param) const;
+ void set_param(PhysicsServer3D::PinJointParam p_param, double p_value);
+
+ float get_applied_force() const;
+
+ virtual void rebuild() override;
+};
+
+#endif // JOLT_PIN_JOINT_3D_H
diff --git a/modules/jolt_physics/joints/jolt_slider_joint_3d.cpp b/modules/jolt_physics/joints/jolt_slider_joint_3d.cpp
new file mode 100644
index 000000000000..5bf119fb8e50
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_slider_joint_3d.cpp
@@ -0,0 +1,544 @@
+/**************************************************************************/
+/* jolt_slider_joint_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_slider_joint_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_body_3d.h"
+#include "../spaces/jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Constraints/FixedConstraint.h"
+#include "Jolt/Physics/Constraints/SliderConstraint.h"
+
+namespace {
+
+constexpr double DEFAULT_LINEAR_LIMIT_SOFTNESS = 1.0;
+constexpr double DEFAULT_LINEAR_LIMIT_RESTITUTION = 0.7;
+constexpr double DEFAULT_LINEAR_LIMIT_DAMPING = 1.0;
+
+constexpr double DEFAULT_LINEAR_MOTION_SOFTNESS = 1.0;
+constexpr double DEFAULT_LINEAR_MOTION_RESTITUTION = 0.7;
+constexpr double DEFAULT_LINEAR_MOTION_DAMPING = 0.0;
+
+constexpr double DEFAULT_LINEAR_ORTHO_SOFTNESS = 1.0;
+constexpr double DEFAULT_LINEAR_ORTHO_RESTITUTION = 0.7;
+constexpr double DEFAULT_LINEAR_ORTHO_DAMPING = 1.0;
+
+constexpr double DEFAULT_ANGULAR_LIMIT_UPPER = 0.0;
+constexpr double DEFAULT_ANGULAR_LIMIT_LOWER = 0.0;
+constexpr double DEFAULT_ANGULAR_LIMIT_SOFTNESS = 1.0;
+constexpr double DEFAULT_ANGULAR_LIMIT_RESTITUTION = 0.7;
+constexpr double DEFAULT_ANGULAR_LIMIT_DAMPING = 0.0;
+
+constexpr double DEFAULT_ANGULAR_MOTION_SOFTNESS = 1.0;
+constexpr double DEFAULT_ANGULAR_MOTION_RESTITUTION = 0.7;
+constexpr double DEFAULT_ANGULAR_MOTION_DAMPING = 1.0;
+
+constexpr double DEFAULT_ANGULAR_ORTHO_SOFTNESS = 1.0;
+constexpr double DEFAULT_ANGULAR_ORTHO_RESTITUTION = 0.7;
+constexpr double DEFAULT_ANGULAR_ORTHO_DAMPING = 1.0;
+
+} // namespace
+
+JPH::Constraint *JoltSliderJoint3D::_build_slider(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b, float p_limit) const {
+ JPH::SliderConstraintSettings constraint_settings;
+
+ constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
+ constraint_settings.mAutoDetectPoint = false;
+ constraint_settings.mPoint1 = to_jolt_r(p_shifted_ref_a.origin);
+ constraint_settings.mSliderAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mNormalAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Z));
+ constraint_settings.mPoint2 = to_jolt_r(p_shifted_ref_b.origin);
+ constraint_settings.mSliderAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mNormalAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Z));
+ constraint_settings.mLimitsMin = -p_limit;
+ constraint_settings.mLimitsMax = p_limit;
+
+ if (limit_spring_enabled) {
+ constraint_settings.mLimitsSpringSettings.mFrequency = (float)limit_spring_frequency;
+ constraint_settings.mLimitsSpringSettings.mDamping = (float)limit_spring_damping;
+ }
+
+ if (p_jolt_body_a == nullptr) {
+ return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);
+ } else if (p_jolt_body_b == nullptr) {
+ return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);
+ } else {
+ return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);
+ }
+}
+
+JPH::Constraint *JoltSliderJoint3D::_build_fixed(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) const {
+ JPH::FixedConstraintSettings constraint_settings;
+
+ constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;
+ constraint_settings.mAutoDetectPoint = false;
+ constraint_settings.mPoint1 = to_jolt_r(p_shifted_ref_a.origin);
+ constraint_settings.mAxisX1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mAxisY1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Y));
+ constraint_settings.mPoint2 = to_jolt_r(p_shifted_ref_b.origin);
+ constraint_settings.mAxisX2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));
+ constraint_settings.mAxisY2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Y));
+
+ if (p_jolt_body_a == nullptr) {
+ return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);
+ } else if (p_jolt_body_b == nullptr) {
+ return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);
+ } else {
+ return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);
+ }
+}
+
+void JoltSliderJoint3D::_update_motor_state() {
+ if (unlikely(_is_fixed())) {
+ return;
+ }
+
+ if (JPH::SliderConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ constraint->SetMotorState(motor_enabled ? JPH::EMotorState::Velocity : JPH::EMotorState::Off);
+ }
+}
+
+void JoltSliderJoint3D::_update_motor_velocity() {
+ if (unlikely(_is_fixed())) {
+ return;
+ }
+
+ if (JPH::SliderConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ constraint->SetTargetVelocity((float)motor_target_speed);
+ }
+}
+
+void JoltSliderJoint3D::_update_motor_limit() {
+ if (unlikely(_is_fixed())) {
+ return;
+ }
+
+ if (JPH::SliderConstraint *constraint = static_cast(jolt_ref.GetPtr())) {
+ JPH::MotorSettings &motor_settings = constraint->GetMotorSettings();
+ motor_settings.mMinForceLimit = (float)-motor_max_force;
+ motor_settings.mMaxForceLimit = (float)motor_max_force;
+ }
+}
+
+void JoltSliderJoint3D::_limits_changed() {
+ rebuild();
+ _wake_up_bodies();
+}
+
+void JoltSliderJoint3D::_limit_spring_changed() {
+ rebuild();
+ _wake_up_bodies();
+}
+
+void JoltSliderJoint3D::_motor_state_changed() {
+ _update_motor_state();
+ _wake_up_bodies();
+}
+
+void JoltSliderJoint3D::_motor_speed_changed() {
+ _update_motor_velocity();
+ _wake_up_bodies();
+}
+
+void JoltSliderJoint3D::_motor_limit_changed() {
+ _update_motor_limit();
+ _wake_up_bodies();
+}
+
+JoltSliderJoint3D::JoltSliderJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b) :
+ JoltJoint3D(p_old_joint, p_body_a, p_body_b, p_local_ref_a, p_local_ref_b) {
+ rebuild();
+}
+
+double JoltSliderJoint3D::get_param(PhysicsServer3D::SliderJointParam p_param) const {
+ switch (p_param) {
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_UPPER: {
+ return limit_upper;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_LOWER: {
+ return limit_lower;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS: {
+ return DEFAULT_LINEAR_LIMIT_SOFTNESS;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION: {
+ return DEFAULT_LINEAR_LIMIT_RESTITUTION;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_DAMPING: {
+ return DEFAULT_LINEAR_LIMIT_DAMPING;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_MOTION_SOFTNESS: {
+ return DEFAULT_LINEAR_MOTION_SOFTNESS;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_MOTION_RESTITUTION: {
+ return DEFAULT_LINEAR_MOTION_RESTITUTION;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_MOTION_DAMPING: {
+ return DEFAULT_LINEAR_MOTION_DAMPING;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_ORTHOGONAL_SOFTNESS: {
+ return DEFAULT_LINEAR_ORTHO_SOFTNESS;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_ORTHOGONAL_RESTITUTION: {
+ return DEFAULT_LINEAR_ORTHO_RESTITUTION;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_ORTHOGONAL_DAMPING: {
+ return DEFAULT_LINEAR_ORTHO_DAMPING;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_UPPER: {
+ return DEFAULT_ANGULAR_LIMIT_UPPER;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_LOWER: {
+ return DEFAULT_ANGULAR_LIMIT_LOWER;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS: {
+ return DEFAULT_ANGULAR_LIMIT_SOFTNESS;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_RESTITUTION: {
+ return DEFAULT_ANGULAR_LIMIT_RESTITUTION;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING: {
+ return DEFAULT_ANGULAR_LIMIT_DAMPING;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_MOTION_SOFTNESS: {
+ return DEFAULT_ANGULAR_MOTION_SOFTNESS;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_MOTION_RESTITUTION: {
+ return DEFAULT_ANGULAR_MOTION_RESTITUTION;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_MOTION_DAMPING: {
+ return DEFAULT_ANGULAR_MOTION_DAMPING;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: {
+ return DEFAULT_ANGULAR_ORTHO_SOFTNESS;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: {
+ return DEFAULT_ANGULAR_ORTHO_RESTITUTION;
+ }
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: {
+ return DEFAULT_ANGULAR_ORTHO_DAMPING;
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled slider joint parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltSliderJoint3D::set_param(PhysicsServer3D::SliderJointParam p_param, double p_value) {
+ switch (p_param) {
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_UPPER: {
+ limit_upper = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_LOWER: {
+ limit_lower = p_value;
+ _limits_changed();
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_LIMIT_SOFTNESS)) {
+ WARN_PRINT(vformat("Slider joint linear limit softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_LIMIT_RESTITUTION)) {
+ WARN_PRINT(vformat("Slider joint linear limit restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_LIMIT_DAMPING)) {
+ WARN_PRINT(vformat("Slider joint linear limit damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_MOTION_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_MOTION_SOFTNESS)) {
+ WARN_PRINT(vformat("Slider joint linear motion softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_MOTION_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_MOTION_RESTITUTION)) {
+ WARN_PRINT(vformat("Slider joint linear motion restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_MOTION_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_MOTION_DAMPING)) {
+ WARN_PRINT(vformat("Slider joint linear motion damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_ORTHOGONAL_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_ORTHO_SOFTNESS)) {
+ WARN_PRINT(vformat("Slider joint linear ortho softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_ORTHOGONAL_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_ORTHO_RESTITUTION)) {
+ WARN_PRINT(vformat("Slider joint linear ortho restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_LINEAR_ORTHOGONAL_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_LINEAR_ORTHO_DAMPING)) {
+ WARN_PRINT(vformat("Slider joint linear ortho damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_UPPER: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_LIMIT_UPPER)) {
+ WARN_PRINT(vformat("Slider joint angular limits are not supported when using Jolt Physics. Any such value will be ignored. Try using Generic6DOFJoint3D instead. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_LOWER: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_LIMIT_LOWER)) {
+ WARN_PRINT(vformat("Slider joint angular limits are not supported when using Jolt Physics. Any such value will be ignored. Try using Generic6DOFJoint3D instead. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_LIMIT_SOFTNESS)) {
+ WARN_PRINT(vformat("Slider joint angular limit softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_LIMIT_RESTITUTION)) {
+ WARN_PRINT(vformat("Slider joint angular limit restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_LIMIT_DAMPING)) {
+ WARN_PRINT(vformat("Slider joint angular limit damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_MOTION_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_MOTION_SOFTNESS)) {
+ WARN_PRINT(vformat("Slider joint angular motion softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_MOTION_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_MOTION_RESTITUTION)) {
+ WARN_PRINT(vformat("Slider joint angular motion restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_MOTION_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_MOTION_DAMPING)) {
+ WARN_PRINT(vformat("Slider joint angular motion damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_ORTHO_SOFTNESS)) {
+ WARN_PRINT(vformat("Slider joint angular ortho softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_ORTHO_RESTITUTION)) {
+ WARN_PRINT(vformat("Slider joint angular ortho restitution is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ case PhysicsServer3D::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING: {
+ if (!Math::is_equal_approx(p_value, DEFAULT_ANGULAR_ORTHO_DAMPING)) {
+ WARN_PRINT(vformat("Slider joint angular ortho damping is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));
+ }
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled slider joint parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+double JoltSliderJoint3D::get_jolt_param(JoltParameter p_param) const {
+ switch (p_param) {
+ case JoltPhysicsServer3D::SLIDER_JOINT_LIMIT_SPRING_FREQUENCY: {
+ return limit_spring_frequency;
+ }
+ case JoltPhysicsServer3D::SLIDER_JOINT_LIMIT_SPRING_DAMPING: {
+ return limit_spring_damping;
+ }
+ case JoltPhysicsServer3D::SLIDER_JOINT_MOTOR_TARGET_VELOCITY: {
+ return motor_target_speed;
+ }
+ case JoltPhysicsServer3D::SLIDER_JOINT_MOTOR_MAX_FORCE: {
+ return motor_max_force;
+ }
+ default: {
+ ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltSliderJoint3D::set_jolt_param(JoltParameter p_param, double p_value) {
+ switch (p_param) {
+ case JoltPhysicsServer3D::SLIDER_JOINT_LIMIT_SPRING_FREQUENCY: {
+ limit_spring_frequency = p_value;
+ _limit_spring_changed();
+ } break;
+ case JoltPhysicsServer3D::SLIDER_JOINT_LIMIT_SPRING_DAMPING: {
+ limit_spring_damping = p_value;
+ _limit_spring_changed();
+ } break;
+ case JoltPhysicsServer3D::SLIDER_JOINT_MOTOR_TARGET_VELOCITY: {
+ motor_target_speed = p_value;
+ _motor_speed_changed();
+ } break;
+ case JoltPhysicsServer3D::SLIDER_JOINT_MOTOR_MAX_FORCE: {
+ motor_max_force = p_value;
+ _motor_limit_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+bool JoltSliderJoint3D::get_jolt_flag(JoltFlag p_flag) const {
+ switch (p_flag) {
+ case JoltPhysicsServer3D::SLIDER_JOINT_FLAG_USE_LIMIT: {
+ return limits_enabled;
+ }
+ case JoltPhysicsServer3D::SLIDER_JOINT_FLAG_USE_LIMIT_SPRING: {
+ return limit_spring_enabled;
+ }
+ case JoltPhysicsServer3D::SLIDER_JOINT_FLAG_ENABLE_MOTOR: {
+ return motor_enabled;
+ }
+ default: {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ }
+ }
+}
+
+void JoltSliderJoint3D::set_jolt_flag(JoltFlag p_flag, bool p_enabled) {
+ switch (p_flag) {
+ case JoltPhysicsServer3D::SLIDER_JOINT_FLAG_USE_LIMIT: {
+ limits_enabled = p_enabled;
+ _limits_changed();
+ } break;
+ case JoltPhysicsServer3D::SLIDER_JOINT_FLAG_USE_LIMIT_SPRING: {
+ limit_spring_enabled = p_enabled;
+ _limit_spring_changed();
+ } break;
+ case JoltPhysicsServer3D::SLIDER_JOINT_FLAG_ENABLE_MOTOR: {
+ motor_enabled = p_enabled;
+ _motor_state_changed();
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));
+ } break;
+ }
+}
+
+float JoltSliderJoint3D::get_applied_force() const {
+ ERR_FAIL_NULL_V(jolt_ref, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ if (_is_fixed()) {
+ JPH::FixedConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ return constraint->GetTotalLambdaPosition().Length() / last_step;
+ } else {
+ JPH::SliderConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ const JPH::Vec3 total_lambda = JPH::Vec3(constraint->GetTotalLambdaPosition()[0], constraint->GetTotalLambdaPosition()[1], constraint->GetTotalLambdaPositionLimits() + constraint->GetTotalLambdaMotor());
+ return total_lambda.Length() / last_step;
+ }
+}
+
+float JoltSliderJoint3D::get_applied_torque() const {
+ ERR_FAIL_NULL_V(jolt_ref, 0.0f);
+
+ JoltSpace3D *space = get_space();
+ ERR_FAIL_NULL_V(space, 0.0f);
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return 0.0f;
+ }
+
+ if (_is_fixed()) {
+ JPH::FixedConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ return constraint->GetTotalLambdaRotation().Length() / last_step;
+ } else {
+ JPH::SliderConstraint *constraint = static_cast(jolt_ref.GetPtr());
+ return constraint->GetTotalLambdaRotation().Length() / last_step;
+ }
+}
+
+void JoltSliderJoint3D::rebuild() {
+ destroy();
+
+ JoltSpace3D *space = get_space();
+
+ if (space == nullptr) {
+ return;
+ }
+
+ const JPH::BodyID body_ids[2] = {
+ body_a != nullptr ? body_a->get_jolt_id() : JPH::BodyID(),
+ body_b != nullptr ? body_b->get_jolt_id() : JPH::BodyID()
+ };
+
+ const JoltWritableBodies3D jolt_bodies = space->write_bodies(body_ids, 2);
+
+ JPH::Body *jolt_body_a = static_cast(jolt_bodies[0]);
+ JPH::Body *jolt_body_b = static_cast(jolt_bodies[1]);
+
+ ERR_FAIL_COND(jolt_body_a == nullptr && jolt_body_b == nullptr);
+
+ float ref_shift = 0.0f;
+ float limit = FLT_MAX;
+
+ if (limits_enabled && limit_lower <= limit_upper) {
+ const double limit_midpoint = (limit_lower + limit_upper) / 2.0f;
+
+ ref_shift = float(-limit_midpoint);
+ limit = float(limit_upper - limit_midpoint);
+ }
+
+ Transform3D shifted_ref_a;
+ Transform3D shifted_ref_b;
+
+ _shift_reference_frames(Vector3(ref_shift, 0.0f, 0.0f), Vector3(), shifted_ref_a, shifted_ref_b);
+
+ if (_is_fixed()) {
+ jolt_ref = _build_fixed(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b);
+ } else {
+ jolt_ref = _build_slider(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b, limit);
+ }
+
+ space->add_joint(this);
+
+ _update_enabled();
+ _update_iterations();
+ _update_motor_state();
+ _update_motor_velocity();
+ _update_motor_limit();
+}
diff --git a/modules/jolt_physics/joints/jolt_slider_joint_3d.h b/modules/jolt_physics/joints/jolt_slider_joint_3d.h
new file mode 100644
index 000000000000..31ed1a4c3a30
--- /dev/null
+++ b/modules/jolt_physics/joints/jolt_slider_joint_3d.h
@@ -0,0 +1,96 @@
+/**************************************************************************/
+/* jolt_slider_joint_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_SLIDER_JOINT_3D_H
+#define JOLT_SLIDER_JOINT_3D_H
+
+#include "../jolt_physics_server_3d.h"
+#include "jolt_joint_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Constraints/SliderConstraint.h"
+
+class JoltSliderJoint3D final : public JoltJoint3D {
+ typedef PhysicsServer3D::SliderJointParam Parameter;
+ typedef JoltPhysicsServer3D::SliderJointParamJolt JoltParameter;
+ typedef JoltPhysicsServer3D::SliderJointFlagJolt JoltFlag;
+
+ double limit_upper = 0.0;
+ double limit_lower = 0.0;
+
+ double limit_spring_frequency = 0.0;
+ double limit_spring_damping = 0.0;
+
+ double motor_target_speed = 0.0f;
+ double motor_max_force = FLT_MAX;
+
+ bool limits_enabled = true;
+ bool limit_spring_enabled = false;
+
+ bool motor_enabled = false;
+
+ JPH::Constraint *_build_slider(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b, float p_limit) const;
+ JPH::Constraint *_build_fixed(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) const;
+
+ bool _is_sprung() const { return limit_spring_enabled && limit_spring_frequency > 0.0; }
+ bool _is_fixed() const { return limits_enabled && limit_lower == limit_upper && !_is_sprung(); }
+
+ void _update_motor_state();
+ void _update_motor_velocity();
+ void _update_motor_limit();
+
+ void _limits_changed();
+ void _limit_spring_changed();
+ void _motor_state_changed();
+ void _motor_speed_changed();
+ void _motor_limit_changed();
+
+public:
+ JoltSliderJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b);
+
+ virtual PhysicsServer3D::JointType get_type() const override { return PhysicsServer3D::JOINT_TYPE_SLIDER; }
+
+ double get_param(PhysicsServer3D::SliderJointParam p_param) const;
+ void set_param(PhysicsServer3D::SliderJointParam p_param, double p_value);
+
+ double get_jolt_param(JoltParameter p_param) const;
+ void set_jolt_param(JoltParameter p_param, double p_value);
+
+ bool get_jolt_flag(JoltFlag p_flag) const;
+ void set_jolt_flag(JoltFlag p_flag, bool p_enabled);
+
+ float get_applied_force() const;
+ float get_applied_torque() const;
+
+ virtual void rebuild() override;
+};
+
+#endif // JOLT_SLIDER_JOINT_3D_H
diff --git a/modules/jolt_physics/jolt_globals.cpp b/modules/jolt_physics/jolt_globals.cpp
new file mode 100644
index 000000000000..670042c9d03a
--- /dev/null
+++ b/modules/jolt_physics/jolt_globals.cpp
@@ -0,0 +1,122 @@
+/**************************************************************************/
+/* jolt_globals.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_globals.h"
+
+#include "objects/jolt_group_filter.h"
+#include "shapes/jolt_custom_double_sided_shape.h"
+#include "shapes/jolt_custom_ray_shape.h"
+#include "shapes/jolt_custom_user_data_shape.h"
+
+#include "core/os/memory.h"
+#include "core/string/print_string.h"
+#include "core/variant/variant.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/RegisterTypes.h"
+
+#include
+
+void *jolt_alloc(size_t p_size) {
+ return Memory::alloc_static(p_size);
+}
+
+void *jolt_realloc(void *p_mem, size_t p_old_size, size_t p_new_size) {
+ return Memory::realloc_static(p_mem, p_new_size);
+}
+
+void jolt_free(void *p_mem) {
+ Memory::free_static(p_mem);
+}
+
+void *jolt_aligned_alloc(size_t p_size, size_t p_alignment) {
+ return Memory::alloc_aligned_static(p_size, p_alignment);
+}
+
+void jolt_aligned_free(void *p_mem) {
+ Memory::free_aligned_static(p_mem);
+}
+
+#ifdef JPH_ENABLE_ASSERTS
+
+void jolt_trace(const char *p_format, ...) {
+ va_list args;
+ va_start(args, p_format);
+ char buffer[1024] = { '\0' };
+ vsnprintf(buffer, sizeof(buffer), p_format, args);
+ va_end(args);
+ print_verbose(buffer);
+}
+
+bool jolt_assert(const char *p_expr, const char *p_msg, const char *p_file, uint32_t p_line) {
+ ERR_PRINT(vformat("Jolt Physics assertion '%s' failed with message '%s' at '%s:%d'", p_expr, p_msg != nullptr ? p_msg : "", p_file, p_line));
+ return false;
+}
+
+#endif
+
+void jolt_initialize() {
+ JPH::Allocate = &jolt_alloc;
+ JPH::Reallocate = &jolt_realloc;
+ JPH::Free = &jolt_free;
+ JPH::AlignedAllocate = &jolt_aligned_alloc;
+ JPH::AlignedFree = &jolt_aligned_free;
+
+#ifdef JPH_ENABLE_ASSERTS
+ JPH::Trace = &jolt_trace;
+ JPH::AssertFailed = &jolt_assert;
+#endif
+
+ JPH::Factory::sInstance = new JPH::Factory();
+
+ JPH::RegisterTypes();
+
+ JoltCustomRayShape::register_type();
+ JoltCustomUserDataShape::register_type();
+ JoltCustomDoubleSidedShape::register_type();
+
+ JoltGroupFilter::instance = new JoltGroupFilter();
+ JoltGroupFilter::instance->SetEmbedded();
+}
+
+void jolt_deinitialize() {
+ if (JoltGroupFilter::instance != nullptr) {
+ delete JoltGroupFilter::instance;
+ JoltGroupFilter::instance = nullptr;
+ }
+
+ JPH::UnregisterTypes();
+
+ if (JPH::Factory::sInstance != nullptr) {
+ delete JPH::Factory::sInstance;
+ JPH::Factory::sInstance = nullptr;
+ }
+}
diff --git a/modules/jolt_physics/jolt_globals.h b/modules/jolt_physics/jolt_globals.h
new file mode 100644
index 000000000000..fd3c7708f019
--- /dev/null
+++ b/modules/jolt_physics/jolt_globals.h
@@ -0,0 +1,37 @@
+/**************************************************************************/
+/* jolt_globals.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_GLOBALS_H
+#define JOLT_GLOBALS_H
+
+void jolt_initialize();
+void jolt_deinitialize();
+
+#endif // JOLT_GLOBALS_H
diff --git a/modules/jolt_physics/jolt_physics_server_3d.cpp b/modules/jolt_physics/jolt_physics_server_3d.cpp
new file mode 100644
index 000000000000..234623a6bed5
--- /dev/null
+++ b/modules/jolt_physics/jolt_physics_server_3d.cpp
@@ -0,0 +1,1979 @@
+/**************************************************************************/
+/* jolt_physics_server_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_physics_server_3d.h"
+
+#include "joints/jolt_cone_twist_joint_3d.h"
+#include "joints/jolt_generic_6dof_joint_3d.h"
+#include "joints/jolt_hinge_joint_3d.h"
+#include "joints/jolt_joint_3d.h"
+#include "joints/jolt_pin_joint_3d.h"
+#include "joints/jolt_slider_joint_3d.h"
+#include "objects/jolt_area_3d.h"
+#include "objects/jolt_body_3d.h"
+#include "objects/jolt_soft_body_3d.h"
+#include "servers/physics_server_3d_wrap_mt.h"
+#include "shapes/jolt_box_shape_3d.h"
+#include "shapes/jolt_capsule_shape_3d.h"
+#include "shapes/jolt_concave_polygon_shape_3d.h"
+#include "shapes/jolt_convex_polygon_shape_3d.h"
+#include "shapes/jolt_cylinder_shape_3d.h"
+#include "shapes/jolt_height_map_shape_3d.h"
+#include "shapes/jolt_separation_ray_shape_3d.h"
+#include "shapes/jolt_sphere_shape_3d.h"
+#include "shapes/jolt_world_boundary_shape_3d.h"
+#include "spaces/jolt_job_system.h"
+#include "spaces/jolt_physics_direct_space_state_3d.h"
+#include "spaces/jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+JoltPhysicsServer3D::JoltPhysicsServer3D(bool p_on_separate_thread) :
+ on_separate_thread(p_on_separate_thread) {
+ singleton = this;
+}
+
+JoltPhysicsServer3D::~JoltPhysicsServer3D() {
+ if (singleton == this) {
+ singleton = nullptr;
+ }
+}
+
+RID JoltPhysicsServer3D::world_boundary_shape_create() {
+ JoltShape3D *shape = memnew(JoltWorldBoundaryShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::separation_ray_shape_create() {
+ JoltShape3D *shape = memnew(JoltSeparationRayShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::sphere_shape_create() {
+ JoltShape3D *shape = memnew(JoltSphereShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::box_shape_create() {
+ JoltShape3D *shape = memnew(JoltBoxShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::capsule_shape_create() {
+ JoltShape3D *shape = memnew(JoltCapsuleShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::cylinder_shape_create() {
+ JoltShape3D *shape = memnew(JoltCylinderShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::convex_polygon_shape_create() {
+ JoltShape3D *shape = memnew(JoltConvexPolygonShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::concave_polygon_shape_create() {
+ JoltShape3D *shape = memnew(JoltConcavePolygonShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::heightmap_shape_create() {
+ JoltShape3D *shape = memnew(JoltHeightMapShape3D);
+ RID rid = shape_owner.make_rid(shape);
+ shape->set_rid(rid);
+ return rid;
+}
+
+RID JoltPhysicsServer3D::custom_shape_create() {
+ ERR_FAIL_V_MSG(RID(), "Custom shapes are not supported.");
+}
+
+void JoltPhysicsServer3D::shape_set_data(RID p_shape, const Variant &p_data) {
+ JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL(shape);
+
+ shape->set_data(p_data);
+}
+
+Variant JoltPhysicsServer3D::shape_get_data(RID p_shape) const {
+ const JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL_V(shape, Variant());
+
+ return shape->get_data();
+}
+
+void JoltPhysicsServer3D::shape_set_custom_solver_bias(RID p_shape, real_t p_bias) {
+ JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL(shape);
+
+ shape->set_solver_bias((float)p_bias);
+}
+
+PhysicsServer3D::ShapeType JoltPhysicsServer3D::shape_get_type(RID p_shape) const {
+ const JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL_V(shape, SHAPE_CUSTOM);
+
+ return shape->get_type();
+}
+
+void JoltPhysicsServer3D::shape_set_margin(RID p_shape, real_t p_margin) {
+ JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL(shape);
+
+ shape->set_margin((float)p_margin);
+}
+
+real_t JoltPhysicsServer3D::shape_get_margin(RID p_shape) const {
+ const JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL_V(shape, 0.0);
+
+ return (real_t)shape->get_margin();
+}
+
+real_t JoltPhysicsServer3D::shape_get_custom_solver_bias(RID p_shape) const {
+ const JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL_V(shape, 0.0);
+
+ return (real_t)shape->get_solver_bias();
+}
+
+RID JoltPhysicsServer3D::space_create() {
+ JoltSpace3D *space = memnew(JoltSpace3D(job_system));
+ RID rid = space_owner.make_rid(space);
+ space->set_rid(rid);
+
+ const RID default_area_rid = area_create();
+ JoltArea3D *default_area = area_owner.get_or_null(default_area_rid);
+ ERR_FAIL_NULL_V(default_area, RID());
+ space->set_default_area(default_area);
+ default_area->set_space(space);
+
+ return rid;
+}
+
+void JoltPhysicsServer3D::space_set_active(RID p_space, bool p_active) {
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL(space);
+
+ if (p_active) {
+ space->set_active(true);
+ active_spaces.insert(space);
+ } else {
+ space->set_active(false);
+ active_spaces.erase(space);
+ }
+}
+
+bool JoltPhysicsServer3D::space_is_active(RID p_space) const {
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL_V(space, false);
+
+ return active_spaces.has(space);
+}
+
+void JoltPhysicsServer3D::space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) {
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL(space);
+
+ space->set_param(p_param, (double)p_value);
+}
+
+real_t JoltPhysicsServer3D::space_get_param(RID p_space, SpaceParameter p_param) const {
+ const JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL_V(space, 0.0);
+
+ return (real_t)space->get_param(p_param);
+}
+
+PhysicsDirectSpaceState3D *JoltPhysicsServer3D::space_get_direct_state(RID p_space) {
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL_V(space, nullptr);
+ ERR_FAIL_COND_V_MSG((on_separate_thread && !doing_sync) || space->is_stepping(), nullptr, "Space state is inaccessible right now, wait for iteration or physics process notification.");
+
+ return space->get_direct_state();
+}
+
+void JoltPhysicsServer3D::space_set_debug_contacts(RID p_space, int p_max_contacts) {
+#ifdef DEBUG_ENABLED
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL(space);
+
+ space->set_max_debug_contacts(p_max_contacts);
+#endif
+}
+
+PackedVector3Array JoltPhysicsServer3D::space_get_contacts(RID p_space) const {
+#ifdef DEBUG_ENABLED
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL_V(space, PackedVector3Array());
+
+ return space->get_debug_contacts();
+#else
+ return PackedVector3Array();
+#endif
+}
+
+int JoltPhysicsServer3D::space_get_contact_count(RID p_space) const {
+#ifdef DEBUG_ENABLED
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL_V(space, 0);
+
+ return space->get_debug_contact_count();
+#else
+ return 0;
+#endif
+}
+
+RID JoltPhysicsServer3D::area_create() {
+ JoltArea3D *area = memnew(JoltArea3D);
+ RID rid = area_owner.make_rid(area);
+ area->set_rid(rid);
+ return rid;
+}
+
+void JoltPhysicsServer3D::area_set_space(RID p_area, RID p_space) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ JoltSpace3D *space = nullptr;
+
+ if (p_space.is_valid()) {
+ space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL(space);
+ }
+
+ area->set_space(space);
+}
+
+RID JoltPhysicsServer3D::area_get_space(RID p_area) const {
+ const JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL_V(area, RID());
+
+ const JoltSpace3D *space = area->get_space();
+
+ if (space == nullptr) {
+ return RID();
+ }
+
+ return space->get_rid();
+}
+
+void JoltPhysicsServer3D::area_add_shape(RID p_area, RID p_shape, const Transform3D &p_transform, bool p_disabled) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL(shape);
+
+ area->add_shape(shape, p_transform, p_disabled);
+}
+
+void JoltPhysicsServer3D::area_set_shape(RID p_area, int p_shape_idx, RID p_shape) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL(shape);
+
+ area->set_shape(p_shape_idx, shape);
+}
+
+RID JoltPhysicsServer3D::area_get_shape(RID p_area, int p_shape_idx) const {
+ const JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL_V(area, RID());
+
+ const JoltShape3D *shape = area->get_shape(p_shape_idx);
+ ERR_FAIL_NULL_V(shape, RID());
+
+ return shape->get_rid();
+}
+
+void JoltPhysicsServer3D::area_set_shape_transform(RID p_area, int p_shape_idx, const Transform3D &p_transform) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_shape_transform(p_shape_idx, p_transform);
+}
+
+Transform3D JoltPhysicsServer3D::area_get_shape_transform(RID p_area, int p_shape_idx) const {
+ const JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL_V(area, Transform3D());
+
+ return area->get_shape_transform_scaled(p_shape_idx);
+}
+
+int JoltPhysicsServer3D::area_get_shape_count(RID p_area) const {
+ const JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL_V(area, 0);
+
+ return area->get_shape_count();
+}
+
+void JoltPhysicsServer3D::area_remove_shape(RID p_area, int p_shape_idx) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->remove_shape(p_shape_idx);
+}
+
+void JoltPhysicsServer3D::area_clear_shapes(RID p_area) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->clear_shapes();
+}
+
+void JoltPhysicsServer3D::area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_shape_disabled(p_shape_idx, p_disabled);
+}
+
+void JoltPhysicsServer3D::area_attach_object_instance_id(RID p_area, ObjectID p_id) {
+ RID area_rid = p_area;
+
+ if (space_owner.owns(area_rid)) {
+ const JoltSpace3D *space = space_owner.get_or_null(area_rid);
+ area_rid = space->get_default_area()->get_rid();
+ }
+
+ JoltArea3D *area = area_owner.get_or_null(area_rid);
+ ERR_FAIL_NULL(area);
+
+ area->set_instance_id(p_id);
+}
+
+ObjectID JoltPhysicsServer3D::area_get_object_instance_id(RID p_area) const {
+ RID area_rid = p_area;
+
+ if (space_owner.owns(area_rid)) {
+ const JoltSpace3D *space = space_owner.get_or_null(area_rid);
+ area_rid = space->get_default_area()->get_rid();
+ }
+
+ JoltArea3D *area = area_owner.get_or_null(area_rid);
+ ERR_FAIL_NULL_V(area, ObjectID());
+
+ return area->get_instance_id();
+}
+
+void JoltPhysicsServer3D::area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) {
+ RID area_rid = p_area;
+
+ if (space_owner.owns(area_rid)) {
+ const JoltSpace3D *space = space_owner.get_or_null(area_rid);
+ area_rid = space->get_default_area()->get_rid();
+ }
+
+ JoltArea3D *area = area_owner.get_or_null(area_rid);
+ ERR_FAIL_NULL(area);
+
+ area->set_param(p_param, p_value);
+}
+
+Variant JoltPhysicsServer3D::area_get_param(RID p_area, AreaParameter p_param) const {
+ RID area_rid = p_area;
+
+ if (space_owner.owns(area_rid)) {
+ const JoltSpace3D *space = space_owner.get_or_null(area_rid);
+ area_rid = space->get_default_area()->get_rid();
+ }
+
+ JoltArea3D *area = area_owner.get_or_null(area_rid);
+ ERR_FAIL_NULL_V(area, Variant());
+
+ return area->get_param(p_param);
+}
+
+void JoltPhysicsServer3D::area_set_transform(RID p_area, const Transform3D &p_transform) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ return area->set_transform(p_transform);
+}
+
+Transform3D JoltPhysicsServer3D::area_get_transform(RID p_area) const {
+ const JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL_V(area, Transform3D());
+
+ return area->get_transform_scaled();
+}
+
+void JoltPhysicsServer3D::area_set_collision_mask(RID p_area, uint32_t p_mask) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_collision_mask(p_mask);
+}
+
+uint32_t JoltPhysicsServer3D::area_get_collision_mask(RID p_area) const {
+ const JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL_V(area, 0);
+
+ return area->get_collision_mask();
+}
+
+void JoltPhysicsServer3D::area_set_collision_layer(RID p_area, uint32_t p_layer) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_collision_layer(p_layer);
+}
+
+uint32_t JoltPhysicsServer3D::area_get_collision_layer(RID p_area) const {
+ const JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL_V(area, 0);
+
+ return area->get_collision_layer();
+}
+
+void JoltPhysicsServer3D::area_set_monitorable(RID p_area, bool p_monitorable) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_monitorable(p_monitorable);
+}
+
+void JoltPhysicsServer3D::area_set_monitor_callback(RID p_area, const Callable &p_callback) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_body_monitor_callback(p_callback);
+}
+
+void JoltPhysicsServer3D::area_set_area_monitor_callback(RID p_area, const Callable &p_callback) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_area_monitor_callback(p_callback);
+}
+
+void JoltPhysicsServer3D::area_set_ray_pickable(RID p_area, bool p_enable) {
+ JoltArea3D *area = area_owner.get_or_null(p_area);
+ ERR_FAIL_NULL(area);
+
+ area->set_pickable(p_enable);
+}
+
+RID JoltPhysicsServer3D::body_create() {
+ JoltBody3D *body = memnew(JoltBody3D);
+ RID rid = body_owner.make_rid(body);
+ body->set_rid(rid);
+ return rid;
+}
+
+void JoltPhysicsServer3D::body_set_space(RID p_body, RID p_space) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ JoltSpace3D *space = nullptr;
+
+ if (p_space.is_valid()) {
+ space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL(space);
+ }
+
+ body->set_space(space);
+}
+
+RID JoltPhysicsServer3D::body_get_space(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, RID());
+
+ const JoltSpace3D *space = body->get_space();
+
+ if (space == nullptr) {
+ return RID();
+ }
+
+ return space->get_rid();
+}
+
+void JoltPhysicsServer3D::body_set_mode(RID p_body, BodyMode p_mode) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_mode(p_mode);
+}
+
+PhysicsServer3D::BodyMode JoltPhysicsServer3D::body_get_mode(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, BODY_MODE_STATIC);
+
+ return body->get_mode();
+}
+
+void JoltPhysicsServer3D::body_add_shape(RID p_body, RID p_shape, const Transform3D &p_transform, bool p_disabled) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL(shape);
+
+ body->add_shape(shape, p_transform, p_disabled);
+}
+
+void JoltPhysicsServer3D::body_set_shape(RID p_body, int p_shape_idx, RID p_shape) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ JoltShape3D *shape = shape_owner.get_or_null(p_shape);
+ ERR_FAIL_NULL(shape);
+
+ body->set_shape(p_shape_idx, shape);
+}
+
+RID JoltPhysicsServer3D::body_get_shape(RID p_body, int p_shape_idx) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, RID());
+
+ const JoltShape3D *shape = body->get_shape(p_shape_idx);
+ ERR_FAIL_NULL_V(shape, RID());
+
+ return shape->get_rid();
+}
+
+void JoltPhysicsServer3D::body_set_shape_transform(RID p_body, int p_shape_idx, const Transform3D &p_transform) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_shape_transform(p_shape_idx, p_transform);
+}
+
+Transform3D JoltPhysicsServer3D::body_get_shape_transform(RID p_body, int p_shape_idx) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, Transform3D());
+
+ return body->get_shape_transform_scaled(p_shape_idx);
+}
+
+int JoltPhysicsServer3D::body_get_shape_count(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0);
+
+ return body->get_shape_count();
+}
+
+void JoltPhysicsServer3D::body_remove_shape(RID p_body, int p_shape_idx) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->remove_shape(p_shape_idx);
+}
+
+void JoltPhysicsServer3D::body_clear_shapes(RID p_body) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->clear_shapes();
+}
+
+void JoltPhysicsServer3D::body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_shape_disabled(p_shape_idx, p_disabled);
+}
+
+void JoltPhysicsServer3D::body_attach_object_instance_id(RID p_body, ObjectID p_id) {
+ if (JoltBody3D *body = body_owner.get_or_null(p_body)) {
+ body->set_instance_id(p_id);
+ } else if (JoltSoftBody3D *soft_body = soft_body_owner.get_or_null(p_body)) {
+ soft_body->set_instance_id(p_id);
+ } else {
+ ERR_FAIL();
+ }
+}
+
+ObjectID JoltPhysicsServer3D::body_get_object_instance_id(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, ObjectID());
+
+ return body->get_instance_id();
+}
+
+void JoltPhysicsServer3D::body_set_enable_continuous_collision_detection(RID p_body, bool p_enable) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_ccd_enabled(p_enable);
+}
+
+bool JoltPhysicsServer3D::body_is_continuous_collision_detection_enabled(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, false);
+
+ return body->is_ccd_enabled();
+}
+
+void JoltPhysicsServer3D::body_set_collision_layer(RID p_body, uint32_t p_layer) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_collision_layer(p_layer);
+}
+
+uint32_t JoltPhysicsServer3D::body_get_collision_layer(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0);
+
+ return body->get_collision_layer();
+}
+
+void JoltPhysicsServer3D::body_set_collision_mask(RID p_body, uint32_t p_mask) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_collision_mask(p_mask);
+}
+
+uint32_t JoltPhysicsServer3D::body_get_collision_mask(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0);
+
+ return body->get_collision_mask();
+}
+
+void JoltPhysicsServer3D::body_set_collision_priority(RID p_body, real_t p_priority) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_collision_priority((float)p_priority);
+}
+
+real_t JoltPhysicsServer3D::body_get_collision_priority(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0.0);
+
+ return (real_t)body->get_collision_priority();
+}
+
+void JoltPhysicsServer3D::body_set_user_flags(RID p_body, uint32_t p_flags) {
+ WARN_PRINT("Body user flags are not supported. Any such value will be ignored.");
+}
+
+uint32_t JoltPhysicsServer3D::body_get_user_flags(RID p_body) const {
+ return 0;
+}
+
+void JoltPhysicsServer3D::body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_param(p_param, p_value);
+}
+
+Variant JoltPhysicsServer3D::body_get_param(RID p_body, BodyParameter p_param) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, Variant());
+
+ return body->get_param(p_param);
+}
+
+void JoltPhysicsServer3D::body_reset_mass_properties(RID p_body) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->reset_mass_properties();
+}
+
+void JoltPhysicsServer3D::body_set_state(RID p_body, BodyState p_state, const Variant &p_value) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_state(p_state, p_value);
+}
+
+Variant JoltPhysicsServer3D::body_get_state(RID p_body, BodyState p_state) const {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, Variant());
+
+ return body->get_state(p_state);
+}
+
+void JoltPhysicsServer3D::body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->apply_central_impulse(p_impulse);
+}
+
+void JoltPhysicsServer3D::body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->apply_impulse(p_impulse, p_position);
+}
+
+void JoltPhysicsServer3D::body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->apply_torque_impulse(p_impulse);
+}
+
+void JoltPhysicsServer3D::body_apply_central_force(RID p_body, const Vector3 &p_force) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->apply_central_force(p_force);
+}
+
+void JoltPhysicsServer3D::body_apply_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->apply_force(p_force, p_position);
+}
+
+void JoltPhysicsServer3D::body_apply_torque(RID p_body, const Vector3 &p_torque) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->apply_torque(p_torque);
+}
+
+void JoltPhysicsServer3D::body_add_constant_central_force(RID p_body, const Vector3 &p_force) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->add_constant_central_force(p_force);
+}
+
+void JoltPhysicsServer3D::body_add_constant_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->add_constant_force(p_force, p_position);
+}
+
+void JoltPhysicsServer3D::body_add_constant_torque(RID p_body, const Vector3 &p_torque) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->add_constant_torque(p_torque);
+}
+
+void JoltPhysicsServer3D::body_set_constant_force(RID p_body, const Vector3 &p_force) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_constant_force(p_force);
+}
+
+Vector3 JoltPhysicsServer3D::body_get_constant_force(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, Vector3());
+
+ return body->get_constant_force();
+}
+
+void JoltPhysicsServer3D::body_set_constant_torque(RID p_body, const Vector3 &p_torque) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_constant_torque(p_torque);
+}
+
+Vector3 JoltPhysicsServer3D::body_get_constant_torque(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, Vector3());
+
+ return body->get_constant_torque();
+}
+
+void JoltPhysicsServer3D::body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_axis_velocity(p_axis_velocity);
+}
+
+void JoltPhysicsServer3D::body_set_axis_lock(RID p_body, BodyAxis p_axis, bool p_lock) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_axis_lock(p_axis, p_lock);
+}
+
+bool JoltPhysicsServer3D::body_is_axis_locked(RID p_body, BodyAxis p_axis) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, false);
+
+ return body->is_axis_locked(p_axis);
+}
+
+void JoltPhysicsServer3D::body_add_collision_exception(RID p_body, RID p_excepted_body) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->add_collision_exception(p_excepted_body);
+}
+
+void JoltPhysicsServer3D::body_remove_collision_exception(RID p_body, RID p_excepted_body) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->remove_collision_exception(p_excepted_body);
+}
+
+void JoltPhysicsServer3D::body_get_collision_exceptions(RID p_body, List *p_exceptions) {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ for (const RID &exception : body->get_collision_exceptions()) {
+ p_exceptions->push_back(exception);
+ }
+}
+
+void JoltPhysicsServer3D::body_set_max_contacts_reported(RID p_body, int p_amount) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_max_contacts_reported(p_amount);
+}
+
+int JoltPhysicsServer3D::body_get_max_contacts_reported(RID p_body) const {
+ const JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0);
+
+ return body->get_max_contacts_reported();
+}
+
+void JoltPhysicsServer3D::body_set_contacts_reported_depth_threshold(RID p_body, real_t p_threshold) {
+ WARN_PRINT("Per-body contact depth threshold is not supported. Any such value will be ignored.");
+}
+
+real_t JoltPhysicsServer3D::body_get_contacts_reported_depth_threshold(RID p_body) const {
+ return 0.0;
+}
+
+void JoltPhysicsServer3D::body_set_omit_force_integration(RID p_body, bool p_enable) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_custom_integrator(p_enable);
+}
+
+bool JoltPhysicsServer3D::body_is_omitting_force_integration(RID p_body) const {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, false);
+
+ return body->has_custom_integrator();
+}
+
+void JoltPhysicsServer3D::body_set_state_sync_callback(RID p_body, const Callable &p_callable) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_state_sync_callback(p_callable);
+}
+
+void JoltPhysicsServer3D::body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_userdata) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_custom_integration_callback(p_callable, p_userdata);
+}
+
+void JoltPhysicsServer3D::body_set_ray_pickable(RID p_body, bool p_enable) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_pickable(p_enable);
+}
+
+bool JoltPhysicsServer3D::body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result) {
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, false);
+
+ JoltSpace3D *space = body->get_space();
+ ERR_FAIL_NULL_V(space, false);
+
+ return space->get_direct_state()->body_test_motion(*body, p_parameters, r_result);
+}
+
+PhysicsDirectBodyState3D *JoltPhysicsServer3D::body_get_direct_state(RID p_body) {
+ ERR_FAIL_COND_V_MSG((on_separate_thread && !doing_sync), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification.");
+
+ JoltBody3D *body = body_owner.get_or_null(p_body);
+ if (unlikely(body == nullptr || body->get_space() == nullptr)) {
+ return nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(body->get_space()->is_stepping(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification.");
+
+ return body->get_direct_state();
+}
+
+RID JoltPhysicsServer3D::soft_body_create() {
+ JoltSoftBody3D *body = memnew(JoltSoftBody3D);
+ RID rid = soft_body_owner.make_rid(body);
+ body->set_rid(rid);
+ return rid;
+}
+
+void JoltPhysicsServer3D::soft_body_update_rendering_server(RID p_body, PhysicsServer3DRenderingServerHandler *p_rendering_server_handler) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->update_rendering_server(p_rendering_server_handler);
+}
+
+void JoltPhysicsServer3D::soft_body_set_space(RID p_body, RID p_space) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ JoltSpace3D *space = nullptr;
+
+ if (p_space.is_valid()) {
+ space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL(space);
+ }
+
+ body->set_space(space);
+}
+
+RID JoltPhysicsServer3D::soft_body_get_space(RID p_body) const {
+ const JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, RID());
+
+ const JoltSpace3D *space = body->get_space();
+
+ if (space == nullptr) {
+ return RID();
+ }
+
+ return space->get_rid();
+}
+
+void JoltPhysicsServer3D::soft_body_set_mesh(RID p_body, RID p_mesh) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_mesh(p_mesh);
+}
+
+AABB JoltPhysicsServer3D::soft_body_get_bounds(RID p_body) const {
+ const JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, AABB());
+
+ return body->get_bounds();
+}
+
+void JoltPhysicsServer3D::soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_collision_layer(p_layer);
+}
+
+uint32_t JoltPhysicsServer3D::soft_body_get_collision_layer(RID p_body) const {
+ const JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0);
+
+ return body->get_collision_layer();
+}
+
+void JoltPhysicsServer3D::soft_body_set_collision_mask(RID p_body, uint32_t p_mask) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_collision_mask(p_mask);
+}
+
+uint32_t JoltPhysicsServer3D::soft_body_get_collision_mask(RID p_body) const {
+ const JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0);
+
+ return body->get_collision_mask();
+}
+
+void JoltPhysicsServer3D::soft_body_add_collision_exception(RID p_body, RID p_excepted_body) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->add_collision_exception(p_excepted_body);
+}
+
+void JoltPhysicsServer3D::soft_body_remove_collision_exception(RID p_body, RID p_excepted_body) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->remove_collision_exception(p_excepted_body);
+}
+
+void JoltPhysicsServer3D::soft_body_get_collision_exceptions(RID p_body, List *p_exceptions) {
+ const JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ for (const RID &exception : body->get_collision_exceptions()) {
+ p_exceptions->push_back(exception);
+ }
+}
+
+void JoltPhysicsServer3D::soft_body_set_state(RID p_body, BodyState p_state, const Variant &p_value) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_state(p_state, p_value);
+}
+
+Variant JoltPhysicsServer3D::soft_body_get_state(RID p_body, BodyState p_state) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, Variant());
+
+ return body->get_state(p_state);
+}
+
+void JoltPhysicsServer3D::soft_body_set_transform(RID p_body, const Transform3D &p_transform) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_transform(p_transform);
+}
+
+void JoltPhysicsServer3D::soft_body_set_ray_pickable(RID p_body, bool p_enable) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_pickable(p_enable);
+}
+
+void JoltPhysicsServer3D::soft_body_set_simulation_precision(RID p_body, int p_precision) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_simulation_precision(p_precision);
+}
+
+int JoltPhysicsServer3D::soft_body_get_simulation_precision(RID p_body) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0);
+
+ return body->get_simulation_precision();
+}
+
+void JoltPhysicsServer3D::soft_body_set_total_mass(RID p_body, real_t p_total_mass) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_mass((float)p_total_mass);
+}
+
+real_t JoltPhysicsServer3D::soft_body_get_total_mass(RID p_body) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0.0);
+
+ return (real_t)body->get_mass();
+}
+
+void JoltPhysicsServer3D::soft_body_set_linear_stiffness(RID p_body, real_t p_coefficient) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_stiffness_coefficient((float)p_coefficient);
+}
+
+real_t JoltPhysicsServer3D::soft_body_get_linear_stiffness(RID p_body) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0.0);
+
+ return (real_t)body->get_stiffness_coefficient();
+}
+
+void JoltPhysicsServer3D::soft_body_set_pressure_coefficient(RID p_body, real_t p_coefficient) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_pressure((float)p_coefficient);
+}
+
+real_t JoltPhysicsServer3D::soft_body_get_pressure_coefficient(RID p_body) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0.0);
+
+ return (real_t)body->get_pressure();
+}
+
+void JoltPhysicsServer3D::soft_body_set_damping_coefficient(RID p_body, real_t p_coefficient) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_linear_damping((float)p_coefficient);
+}
+
+real_t JoltPhysicsServer3D::soft_body_get_damping_coefficient(RID p_body) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0.0);
+
+ return (real_t)body->get_linear_damping();
+}
+
+void JoltPhysicsServer3D::soft_body_set_drag_coefficient(RID p_body, real_t p_coefficient) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ return body->set_drag((float)p_coefficient);
+}
+
+real_t JoltPhysicsServer3D::soft_body_get_drag_coefficient(RID p_body) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, 0.0);
+
+ return (real_t)body->get_drag();
+}
+
+void JoltPhysicsServer3D::soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->set_vertex_position(p_point_index, p_global_position);
+}
+
+Vector3 JoltPhysicsServer3D::soft_body_get_point_global_position(RID p_body, int p_point_index) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, Vector3());
+
+ return body->get_vertex_position(p_point_index);
+}
+
+void JoltPhysicsServer3D::soft_body_remove_all_pinned_points(RID p_body) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ body->unpin_all_vertices();
+}
+
+void JoltPhysicsServer3D::soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL(body);
+
+ if (p_pin) {
+ body->pin_vertex(p_point_index);
+ } else {
+ body->unpin_vertex(p_point_index);
+ }
+}
+
+bool JoltPhysicsServer3D::soft_body_is_point_pinned(RID p_body, int p_point_index) const {
+ JoltSoftBody3D *body = soft_body_owner.get_or_null(p_body);
+ ERR_FAIL_NULL_V(body, false);
+
+ return body->is_vertex_pinned(p_point_index);
+}
+
+RID JoltPhysicsServer3D::joint_create() {
+ JoltJoint3D *joint = memnew(JoltJoint3D);
+ RID rid = joint_owner.make_rid(joint);
+ joint->set_rid(rid);
+ return rid;
+}
+
+void JoltPhysicsServer3D::joint_clear(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ if (joint->get_type() != JOINT_TYPE_MAX) {
+ JoltJoint3D *empty_joint = memnew(JoltJoint3D);
+ empty_joint->set_rid(joint->get_rid());
+
+ memdelete(joint);
+ joint = nullptr;
+
+ joint_owner.replace(p_joint, empty_joint);
+ }
+}
+
+void JoltPhysicsServer3D::joint_make_pin(RID p_joint, RID p_body_a, const Vector3 &p_local_a, RID p_body_b, const Vector3 &p_local_b) {
+ JoltJoint3D *old_joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(old_joint);
+
+ JoltBody3D *body_a = body_owner.get_or_null(p_body_a);
+ ERR_FAIL_NULL(body_a);
+
+ JoltBody3D *body_b = body_owner.get_or_null(p_body_b);
+ ERR_FAIL_COND(body_a == body_b);
+
+ JoltJoint3D *new_joint = memnew(JoltPinJoint3D(*old_joint, body_a, body_b, p_local_a, p_local_b));
+
+ memdelete(old_joint);
+ old_joint = nullptr;
+
+ joint_owner.replace(p_joint, new_joint);
+}
+
+void JoltPhysicsServer3D::pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_PIN);
+ JoltPinJoint3D *pin_joint = static_cast(joint);
+
+ pin_joint->set_param(p_param, (double)p_value);
+}
+
+real_t JoltPhysicsServer3D::pin_joint_get_param(RID p_joint, PinJointParam p_param) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_PIN, 0.0);
+ const JoltPinJoint3D *pin_joint = static_cast(joint);
+
+ return (real_t)pin_joint->get_param(p_param);
+}
+
+void JoltPhysicsServer3D::pin_joint_set_local_a(RID p_joint, const Vector3 &p_local_a) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_PIN);
+ JoltPinJoint3D *pin_joint = static_cast(joint);
+
+ pin_joint->set_local_a(p_local_a);
+}
+
+Vector3 JoltPhysicsServer3D::pin_joint_get_local_a(RID p_joint) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, Vector3());
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_PIN, Vector3());
+ const JoltPinJoint3D *pin_joint = static_cast(joint);
+
+ return pin_joint->get_local_a();
+}
+
+void JoltPhysicsServer3D::pin_joint_set_local_b(RID p_joint, const Vector3 &p_local_b) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_PIN);
+ JoltPinJoint3D *pin_joint = static_cast(joint);
+
+ pin_joint->set_local_b(p_local_b);
+}
+
+Vector3 JoltPhysicsServer3D::pin_joint_get_local_b(RID p_joint) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, Vector3());
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_PIN, Vector3());
+ const JoltPinJoint3D *pin_joint = static_cast(joint);
+
+ return pin_joint->get_local_b();
+}
+
+void JoltPhysicsServer3D::joint_make_hinge(RID p_joint, RID p_body_a, const Transform3D &p_hinge_a, RID p_body_b, const Transform3D &p_hinge_b) {
+ JoltJoint3D *old_joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(old_joint);
+
+ JoltBody3D *body_a = body_owner.get_or_null(p_body_a);
+ ERR_FAIL_NULL(body_a);
+
+ JoltBody3D *body_b = body_owner.get_or_null(p_body_b);
+ ERR_FAIL_COND(body_a == body_b);
+
+ JoltJoint3D *new_joint = memnew(JoltHingeJoint3D(*old_joint, body_a, body_b, p_hinge_a, p_hinge_b));
+
+ memdelete(old_joint);
+ old_joint = nullptr;
+
+ joint_owner.replace(p_joint, new_joint);
+}
+
+void JoltPhysicsServer3D::joint_make_hinge_simple(RID p_joint, RID p_body_a, const Vector3 &p_pivot_a, const Vector3 &p_axis_a, RID p_body_b, const Vector3 &p_pivot_b, const Vector3 &p_axis_b) {
+ ERR_FAIL_MSG("Simple hinge joints are not supported when using Jolt Physics.");
+}
+
+void JoltPhysicsServer3D::hinge_joint_set_param(RID p_joint, HingeJointParam p_param, real_t p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_HINGE);
+ JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->set_param(p_param, (double)p_value);
+}
+
+real_t JoltPhysicsServer3D::hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_HINGE, 0.0);
+ const JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return (real_t)hinge_joint->get_param(p_param);
+}
+
+void JoltPhysicsServer3D::hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_enabled) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_HINGE);
+ JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->set_flag(p_flag, p_enabled);
+}
+
+bool JoltPhysicsServer3D::hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_HINGE, false);
+ const JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->get_flag(p_flag);
+}
+
+void JoltPhysicsServer3D::joint_make_slider(RID p_joint, RID p_body_a, const Transform3D &p_local_ref_a, RID p_body_b, const Transform3D &p_local_ref_b) {
+ JoltJoint3D *old_joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(old_joint);
+
+ JoltBody3D *body_a = body_owner.get_or_null(p_body_a);
+ ERR_FAIL_NULL(body_a);
+
+ JoltBody3D *body_b = body_owner.get_or_null(p_body_b);
+ ERR_FAIL_COND(body_a == body_b);
+
+ JoltJoint3D *new_joint = memnew(JoltSliderJoint3D(*old_joint, body_a, body_b, p_local_ref_a, p_local_ref_b));
+
+ memdelete(old_joint);
+ old_joint = nullptr;
+
+ joint_owner.replace(p_joint, new_joint);
+}
+
+void JoltPhysicsServer3D::slider_joint_set_param(RID p_joint, SliderJointParam p_param, real_t p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_SLIDER);
+ JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->set_param(p_param, (real_t)p_value);
+}
+
+real_t JoltPhysicsServer3D::slider_joint_get_param(RID p_joint, SliderJointParam p_param) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_SLIDER, 0.0);
+ const JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->get_param(p_param);
+}
+
+void JoltPhysicsServer3D::joint_make_cone_twist(RID p_joint, RID p_body_a, const Transform3D &p_local_ref_a, RID p_body_b, const Transform3D &p_local_ref_b) {
+ JoltJoint3D *old_joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(old_joint);
+
+ JoltBody3D *body_a = body_owner.get_or_null(p_body_a);
+ ERR_FAIL_NULL(body_a);
+
+ JoltBody3D *body_b = body_owner.get_or_null(p_body_b);
+ ERR_FAIL_COND(body_a == body_b);
+
+ JoltJoint3D *new_joint = memnew(JoltConeTwistJoint3D(*old_joint, body_a, body_b, p_local_ref_a, p_local_ref_b));
+
+ memdelete(old_joint);
+ old_joint = nullptr;
+
+ joint_owner.replace(p_joint, new_joint);
+}
+
+void JoltPhysicsServer3D::cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, real_t p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_CONE_TWIST);
+ JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return cone_twist_joint->set_param(p_param, (double)p_value);
+}
+
+real_t JoltPhysicsServer3D::cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_CONE_TWIST, 0.0);
+ const JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return (real_t)cone_twist_joint->get_param(p_param);
+}
+
+void JoltPhysicsServer3D::joint_make_generic_6dof(RID p_joint, RID p_body_a, const Transform3D &p_local_ref_a, RID p_body_b, const Transform3D &p_local_ref_b) {
+ JoltJoint3D *old_joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(old_joint);
+
+ JoltBody3D *body_a = body_owner.get_or_null(p_body_a);
+ ERR_FAIL_NULL(body_a);
+
+ JoltBody3D *body_b = body_owner.get_or_null(p_body_b);
+ ERR_FAIL_COND(body_a == body_b);
+
+ JoltJoint3D *new_joint = memnew(JoltGeneric6DOFJoint3D(*old_joint, body_a, body_b, p_local_ref_a, p_local_ref_b));
+
+ memdelete(old_joint);
+ old_joint = nullptr;
+
+ joint_owner.replace(p_joint, new_joint);
+}
+
+void JoltPhysicsServer3D::generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisParam p_param, real_t p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_6DOF);
+ JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->set_param(p_axis, p_param, (double)p_value);
+}
+
+real_t JoltPhysicsServer3D::generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisParam p_param) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, 0.0);
+ const JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return (real_t)g6dof_joint->get_param(p_axis, p_param);
+}
+
+void JoltPhysicsServer3D::generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag, bool p_enable) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_6DOF);
+ JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->set_flag(p_axis, p_flag, p_enable);
+}
+
+bool JoltPhysicsServer3D::generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, false);
+ const JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->get_flag(p_axis, p_flag);
+}
+
+PhysicsServer3D::JointType JoltPhysicsServer3D::joint_get_type(RID p_joint) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, JOINT_TYPE_PIN);
+
+ return joint->get_type();
+}
+
+void JoltPhysicsServer3D::joint_set_solver_priority(RID p_joint, int p_priority) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ joint->set_solver_priority(p_priority);
+}
+
+int JoltPhysicsServer3D::joint_get_solver_priority(RID p_joint) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0);
+
+ return joint->get_solver_priority();
+}
+
+void JoltPhysicsServer3D::joint_disable_collisions_between_bodies(RID p_joint, bool p_disable) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ joint->set_collision_disabled(p_disable);
+}
+
+bool JoltPhysicsServer3D::joint_is_disabled_collisions_between_bodies(RID p_joint) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ return joint->is_collision_disabled();
+}
+
+void JoltPhysicsServer3D::free(RID p_rid) {
+ if (JoltShape3D *shape = shape_owner.get_or_null(p_rid)) {
+ free_shape(shape);
+ } else if (JoltBody3D *body = body_owner.get_or_null(p_rid)) {
+ free_body(body);
+ } else if (JoltJoint3D *joint = joint_owner.get_or_null(p_rid)) {
+ free_joint(joint);
+ } else if (JoltArea3D *area = area_owner.get_or_null(p_rid)) {
+ free_area(area);
+ } else if (JoltSoftBody3D *soft_body = soft_body_owner.get_or_null(p_rid)) {
+ free_soft_body(soft_body);
+ } else if (JoltSpace3D *space = space_owner.get_or_null(p_rid)) {
+ free_space(space);
+ } else {
+ ERR_FAIL_MSG("Failed to free RID: The specified RID has no owner.");
+ }
+}
+
+void JoltPhysicsServer3D::set_active(bool p_active) {
+ active = p_active;
+}
+
+void JoltPhysicsServer3D::init() {
+ job_system = new JoltJobSystem();
+}
+
+void JoltPhysicsServer3D::finish() {
+ if (job_system != nullptr) {
+ delete job_system;
+ job_system = nullptr;
+ }
+}
+
+void JoltPhysicsServer3D::step(real_t p_step) {
+ if (!active) {
+ return;
+ }
+
+ for (JoltSpace3D *active_space : active_spaces) {
+ job_system->pre_step();
+
+ active_space->step((float)p_step);
+
+ job_system->post_step();
+ }
+}
+
+void JoltPhysicsServer3D::sync() {
+ doing_sync = true;
+}
+
+void JoltPhysicsServer3D::end_sync() {
+ doing_sync = false;
+}
+
+void JoltPhysicsServer3D::flush_queries() {
+ if (!active) {
+ return;
+ }
+
+ flushing_queries = true;
+
+ for (JoltSpace3D *space : active_spaces) {
+ space->call_queries();
+ }
+
+ flushing_queries = false;
+
+#ifdef DEBUG_ENABLED
+ job_system->flush_timings();
+#endif
+}
+
+bool JoltPhysicsServer3D::is_flushing_queries() const {
+ return flushing_queries;
+}
+
+int JoltPhysicsServer3D::get_process_info(ProcessInfo p_process_info) {
+ return 0;
+}
+
+void JoltPhysicsServer3D::free_space(JoltSpace3D *p_space) {
+ ERR_FAIL_NULL(p_space);
+
+ free_area(p_space->get_default_area());
+ space_set_active(p_space->get_rid(), false);
+ space_owner.free(p_space->get_rid());
+ memdelete(p_space);
+}
+
+void JoltPhysicsServer3D::free_area(JoltArea3D *p_area) {
+ ERR_FAIL_NULL(p_area);
+
+ p_area->set_space(nullptr);
+ area_owner.free(p_area->get_rid());
+ memdelete(p_area);
+}
+
+void JoltPhysicsServer3D::free_body(JoltBody3D *p_body) {
+ ERR_FAIL_NULL(p_body);
+
+ p_body->set_space(nullptr);
+ body_owner.free(p_body->get_rid());
+ memdelete(p_body);
+}
+
+void JoltPhysicsServer3D::free_soft_body(JoltSoftBody3D *p_body) {
+ ERR_FAIL_NULL(p_body);
+
+ p_body->set_space(nullptr);
+ soft_body_owner.free(p_body->get_rid());
+ memdelete(p_body);
+}
+
+void JoltPhysicsServer3D::free_shape(JoltShape3D *p_shape) {
+ ERR_FAIL_NULL(p_shape);
+
+ p_shape->remove_self();
+ shape_owner.free(p_shape->get_rid());
+ memdelete(p_shape);
+}
+
+void JoltPhysicsServer3D::free_joint(JoltJoint3D *p_joint) {
+ ERR_FAIL_NULL(p_joint);
+
+ joint_owner.free(p_joint->get_rid());
+ memdelete(p_joint);
+}
+
+#ifdef DEBUG_ENABLED
+
+void JoltPhysicsServer3D::dump_debug_snapshots(const String &p_dir) {
+ for (JoltSpace3D *space : active_spaces) {
+ space->dump_debug_snapshot(p_dir);
+ }
+}
+
+void JoltPhysicsServer3D::space_dump_debug_snapshot(RID p_space, const String &p_dir) {
+ JoltSpace3D *space = space_owner.get_or_null(p_space);
+ ERR_FAIL_NULL(space);
+
+ space->dump_debug_snapshot(p_dir);
+}
+
+#endif
+
+bool JoltPhysicsServer3D::joint_get_enabled(RID p_joint) const {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ return joint->is_enabled();
+}
+
+void JoltPhysicsServer3D::joint_set_enabled(RID p_joint, bool p_enabled) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ joint->set_enabled(p_enabled);
+}
+
+int JoltPhysicsServer3D::joint_get_solver_velocity_iterations(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0);
+
+ return joint->get_solver_velocity_iterations();
+}
+
+void JoltPhysicsServer3D::joint_set_solver_velocity_iterations(RID p_joint, int p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ return joint->set_solver_velocity_iterations(p_value);
+}
+
+int JoltPhysicsServer3D::joint_get_solver_position_iterations(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0);
+
+ return joint->get_solver_position_iterations();
+}
+
+void JoltPhysicsServer3D::joint_set_solver_position_iterations(RID p_joint, int p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ return joint->set_solver_position_iterations(p_value);
+}
+
+float JoltPhysicsServer3D::pin_joint_get_applied_force(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_PIN, 0.0);
+ JoltPinJoint3D *pin_joint = static_cast(joint);
+
+ return pin_joint->get_applied_force();
+}
+
+double JoltPhysicsServer3D::hinge_joint_get_jolt_param(RID p_joint, HingeJointParamJolt p_param) const {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_HINGE, 0.0);
+ JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->get_jolt_param(p_param);
+}
+
+void JoltPhysicsServer3D::hinge_joint_set_jolt_param(RID p_joint, HingeJointParamJolt p_param, double p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_HINGE);
+ JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->set_jolt_param(p_param, p_value);
+}
+
+bool JoltPhysicsServer3D::hinge_joint_get_jolt_flag(RID p_joint, HingeJointFlagJolt p_flag) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_HINGE, false);
+ const JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->get_jolt_flag(p_flag);
+}
+
+void JoltPhysicsServer3D::hinge_joint_set_jolt_flag(RID p_joint, HingeJointFlagJolt p_flag, bool p_enabled) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_HINGE);
+ JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->set_jolt_flag(p_flag, p_enabled);
+}
+
+float JoltPhysicsServer3D::hinge_joint_get_applied_force(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_HINGE, 0.0f);
+ JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->get_applied_force();
+}
+
+float JoltPhysicsServer3D::hinge_joint_get_applied_torque(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_HINGE, 0.0f);
+ JoltHingeJoint3D *hinge_joint = static_cast(joint);
+
+ return hinge_joint->get_applied_torque();
+}
+
+double JoltPhysicsServer3D::slider_joint_get_jolt_param(RID p_joint, SliderJointParamJolt p_param) const {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_SLIDER, 0.0);
+ JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->get_jolt_param(p_param);
+}
+
+void JoltPhysicsServer3D::slider_joint_set_jolt_param(RID p_joint, SliderJointParamJolt p_param, double p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_SLIDER);
+ JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->set_jolt_param(p_param, p_value);
+}
+
+bool JoltPhysicsServer3D::slider_joint_get_jolt_flag(RID p_joint, SliderJointFlagJolt p_flag) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_SLIDER, false);
+ const JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->get_jolt_flag(p_flag);
+}
+
+void JoltPhysicsServer3D::slider_joint_set_jolt_flag(RID p_joint, SliderJointFlagJolt p_flag, bool p_enabled) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_SLIDER);
+ JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->set_jolt_flag(p_flag, p_enabled);
+}
+
+float JoltPhysicsServer3D::slider_joint_get_applied_force(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_SLIDER, 0.0f);
+ JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->get_applied_force();
+}
+
+float JoltPhysicsServer3D::slider_joint_get_applied_torque(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_SLIDER, 0.0f);
+ JoltSliderJoint3D *slider_joint = static_cast(joint);
+
+ return slider_joint->get_applied_torque();
+}
+
+double JoltPhysicsServer3D::cone_twist_joint_get_jolt_param(RID p_joint, ConeTwistJointParamJolt p_param) const {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_CONE_TWIST, 0.0);
+ JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return cone_twist_joint->get_jolt_param(p_param);
+}
+
+void JoltPhysicsServer3D::cone_twist_joint_set_jolt_param(RID p_joint, ConeTwistJointParamJolt p_param, double p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_CONE_TWIST);
+ JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return cone_twist_joint->set_jolt_param(p_param, p_value);
+}
+
+bool JoltPhysicsServer3D::cone_twist_joint_get_jolt_flag(RID p_joint, ConeTwistJointFlagJolt p_flag) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_CONE_TWIST, false);
+ const JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return cone_twist_joint->get_jolt_flag(p_flag);
+}
+
+void JoltPhysicsServer3D::cone_twist_joint_set_jolt_flag(RID p_joint, ConeTwistJointFlagJolt p_flag, bool p_enabled) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_CONE_TWIST);
+ JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return cone_twist_joint->set_jolt_flag(p_flag, p_enabled);
+}
+
+float JoltPhysicsServer3D::cone_twist_joint_get_applied_force(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_CONE_TWIST, 0.0f);
+ JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return cone_twist_joint->get_applied_force();
+}
+
+float JoltPhysicsServer3D::cone_twist_joint_get_applied_torque(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_CONE_TWIST, 0.0f);
+ JoltConeTwistJoint3D *cone_twist_joint = static_cast(joint);
+
+ return cone_twist_joint->get_applied_torque();
+}
+
+double JoltPhysicsServer3D::generic_6dof_joint_get_jolt_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParamJolt p_param) const {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, 0.0);
+ JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->get_jolt_param(p_axis, p_param);
+}
+
+void JoltPhysicsServer3D::generic_6dof_joint_set_jolt_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParamJolt p_param, double p_value) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_6DOF);
+ JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->set_jolt_param(p_axis, p_param, p_value);
+}
+
+bool JoltPhysicsServer3D::generic_6dof_joint_get_jolt_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlagJolt p_flag) const {
+ const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, false);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, false);
+ const JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->get_jolt_flag(p_axis, p_flag);
+}
+
+void JoltPhysicsServer3D::generic_6dof_joint_set_jolt_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlagJolt p_flag, bool p_enabled) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL(joint);
+
+ ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_6DOF);
+ JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->set_jolt_flag(p_axis, p_flag, p_enabled);
+}
+
+float JoltPhysicsServer3D::generic_6dof_joint_get_applied_force(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, 0.0f);
+ JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->get_applied_force();
+}
+
+float JoltPhysicsServer3D::generic_6dof_joint_get_applied_torque(RID p_joint) {
+ JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
+ ERR_FAIL_NULL_V(joint, 0.0f);
+
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, 0.0f);
+ JoltGeneric6DOFJoint3D *g6dof_joint = static_cast(joint);
+
+ return g6dof_joint->get_applied_torque();
+}
diff --git a/modules/jolt_physics/jolt_physics_server_3d.h b/modules/jolt_physics/jolt_physics_server_3d.h
new file mode 100644
index 000000000000..00f527e7f649
--- /dev/null
+++ b/modules/jolt_physics/jolt_physics_server_3d.h
@@ -0,0 +1,503 @@
+/**************************************************************************/
+/* jolt_physics_server_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_PHYSICS_SERVER_3D_H
+#define JOLT_PHYSICS_SERVER_3D_H
+
+#include "core/templates/hash_set.h"
+#include "core/templates/rid_owner.h"
+#include "servers/physics_server_3d.h"
+
+class JoltArea3D;
+class JoltBody3D;
+class JoltJobSystem;
+class JoltJoint3D;
+class JoltShape3D;
+class JoltSoftBody3D;
+class JoltSpace3D;
+
+class JoltPhysicsServer3D final : public PhysicsServer3D {
+ GDCLASS(JoltPhysicsServer3D, PhysicsServer3D)
+
+ inline static JoltPhysicsServer3D *singleton = nullptr;
+
+ mutable RID_PtrOwner space_owner;
+ mutable RID_PtrOwner area_owner;
+ mutable RID_PtrOwner body_owner;
+ mutable RID_PtrOwner soft_body_owner;
+ mutable RID_PtrOwner shape_owner;
+ mutable RID_PtrOwner joint_owner;
+
+ HashSet active_spaces;
+
+ JoltJobSystem *job_system = nullptr;
+
+ bool on_separate_thread = false;
+ bool active = true;
+ bool flushing_queries = false;
+ bool doing_sync = false;
+
+public:
+ enum HingeJointParamJolt {
+ HINGE_JOINT_LIMIT_SPRING_FREQUENCY = 100,
+ HINGE_JOINT_LIMIT_SPRING_DAMPING,
+ HINGE_JOINT_MOTOR_MAX_TORQUE,
+ };
+
+ enum HingeJointFlagJolt {
+ HINGE_JOINT_FLAG_USE_LIMIT_SPRING = 100,
+ };
+
+ enum SliderJointParamJolt {
+ SLIDER_JOINT_LIMIT_SPRING_FREQUENCY = 100,
+ SLIDER_JOINT_LIMIT_SPRING_DAMPING,
+ SLIDER_JOINT_MOTOR_TARGET_VELOCITY,
+ SLIDER_JOINT_MOTOR_MAX_FORCE,
+ };
+
+ enum SliderJointFlagJolt {
+ SLIDER_JOINT_FLAG_USE_LIMIT = 100,
+ SLIDER_JOINT_FLAG_USE_LIMIT_SPRING,
+ SLIDER_JOINT_FLAG_ENABLE_MOTOR,
+ };
+
+ enum ConeTwistJointParamJolt {
+ CONE_TWIST_JOINT_SWING_MOTOR_TARGET_VELOCITY_Y = 100,
+ CONE_TWIST_JOINT_SWING_MOTOR_TARGET_VELOCITY_Z,
+ CONE_TWIST_JOINT_TWIST_MOTOR_TARGET_VELOCITY,
+ CONE_TWIST_JOINT_SWING_MOTOR_MAX_TORQUE,
+ CONE_TWIST_JOINT_TWIST_MOTOR_MAX_TORQUE,
+ };
+
+ enum ConeTwistJointFlagJolt {
+ CONE_TWIST_JOINT_FLAG_USE_SWING_LIMIT = 100,
+ CONE_TWIST_JOINT_FLAG_USE_TWIST_LIMIT,
+ CONE_TWIST_JOINT_FLAG_ENABLE_SWING_MOTOR,
+ CONE_TWIST_JOINT_FLAG_ENABLE_TWIST_MOTOR,
+ };
+
+ enum G6DOFJointAxisParamJolt {
+ G6DOF_JOINT_LINEAR_SPRING_FREQUENCY = 100,
+ G6DOF_JOINT_LINEAR_LIMIT_SPRING_FREQUENCY,
+ G6DOF_JOINT_LINEAR_LIMIT_SPRING_DAMPING,
+ G6DOF_JOINT_ANGULAR_SPRING_FREQUENCY,
+ G6DOF_JOINT_LINEAR_SPRING_MAX_FORCE,
+ G6DOF_JOINT_ANGULAR_SPRING_MAX_TORQUE,
+ };
+
+ enum G6DOFJointAxisFlagJolt {
+ G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT_SPRING = 100,
+ G6DOF_JOINT_FLAG_ENABLE_LINEAR_SPRING_FREQUENCY,
+ G6DOF_JOINT_FLAG_ENABLE_ANGULAR_SPRING_FREQUENCY,
+ };
+
+private:
+ static void _bind_methods() {}
+
+public:
+ explicit JoltPhysicsServer3D(bool p_on_separate_thread);
+ ~JoltPhysicsServer3D();
+
+ static JoltPhysicsServer3D *get_singleton() { return singleton; }
+
+ virtual RID world_boundary_shape_create() override;
+ virtual RID separation_ray_shape_create() override;
+ virtual RID sphere_shape_create() override;
+ virtual RID box_shape_create() override;
+ virtual RID capsule_shape_create() override;
+ virtual RID cylinder_shape_create() override;
+ virtual RID convex_polygon_shape_create() override;
+ virtual RID concave_polygon_shape_create() override;
+ virtual RID heightmap_shape_create() override;
+ virtual RID custom_shape_create() override;
+
+ virtual void shape_set_data(RID p_shape, const Variant &p_data) override;
+ virtual Variant shape_get_data(RID p_shape) const override;
+
+ virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias) override;
+
+ virtual void shape_set_margin(RID p_shape, real_t p_margin) override;
+ virtual real_t shape_get_margin(RID p_shape) const override;
+
+ virtual PhysicsServer3D::ShapeType shape_get_type(RID p_shape) const override;
+
+ virtual real_t shape_get_custom_solver_bias(RID p_shape) const override;
+
+ virtual RID space_create() override;
+
+ virtual void space_set_active(RID p_space, bool p_active) override;
+ virtual bool space_is_active(RID p_space) const override;
+
+ virtual void space_set_param(RID p_space, PhysicsServer3D::SpaceParameter p_param, real_t p_value) override;
+ virtual real_t space_get_param(RID p_space, PhysicsServer3D::SpaceParameter p_param) const override;
+
+ virtual PhysicsDirectSpaceState3D *space_get_direct_state(RID p_space) override;
+
+ virtual void space_set_debug_contacts(RID p_space, int p_max_contacts) override;
+ virtual PackedVector3Array space_get_contacts(RID p_space) const override;
+ virtual int space_get_contact_count(RID p_space) const override;
+
+ virtual RID area_create() override;
+
+ virtual void area_set_space(RID p_area, RID p_space) override;
+ virtual RID area_get_space(RID p_area) const override;
+
+ virtual void area_add_shape(RID p_area, RID p_shape, const Transform3D &p_transform, bool p_disabled) override;
+
+ virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape) override;
+ virtual RID area_get_shape(RID p_area, int p_shape_idx) const override;
+
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform3D &p_transform) override;
+ virtual Transform3D area_get_shape_transform(RID p_area, int p_shape_idx) const override;
+
+ virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) override;
+
+ virtual int area_get_shape_count(RID p_area) const override;
+
+ virtual void area_remove_shape(RID p_area, int p_shape_idx) override;
+ virtual void area_clear_shapes(RID p_area) override;
+
+ virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id) override;
+ virtual ObjectID area_get_object_instance_id(RID p_area) const override;
+
+ virtual void area_set_param(RID p_area, PhysicsServer3D::AreaParameter p_param, const Variant &p_value) override;
+ virtual Variant area_get_param(RID p_area, PhysicsServer3D::AreaParameter p_param) const override;
+
+ virtual void area_set_transform(RID p_area, const Transform3D &p_transform) override;
+ virtual Transform3D area_get_transform(RID p_area) const override;
+
+ virtual void area_set_collision_layer(RID p_area, uint32_t p_layer) override;
+ virtual uint32_t area_get_collision_layer(RID p_area) const override;
+
+ virtual void area_set_collision_mask(RID p_area, uint32_t p_mask) override;
+ virtual uint32_t area_get_collision_mask(RID p_area) const override;
+
+ virtual void area_set_monitorable(RID p_area, bool p_monitorable) override;
+
+ virtual void area_set_ray_pickable(RID p_area, bool p_enable) override;
+
+ virtual void area_set_monitor_callback(RID p_area, const Callable &p_callback) override;
+ virtual void area_set_area_monitor_callback(RID p_area, const Callable &p_callback) override;
+
+ virtual RID body_create() override;
+
+ virtual void body_set_space(RID p_body, RID p_space) override;
+ virtual RID body_get_space(RID p_body) const override;
+
+ virtual void body_set_mode(RID p_body, PhysicsServer3D::BodyMode p_mode) override;
+ virtual PhysicsServer3D::BodyMode body_get_mode(RID p_body) const override;
+
+ virtual void body_add_shape(RID p_body, RID p_shape, const Transform3D &p_transform, bool p_disabled) override;
+
+ virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape) override;
+ virtual RID body_get_shape(RID p_body, int p_shape_idx) const override;
+
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform3D &p_transform) override;
+ virtual Transform3D body_get_shape_transform(RID p_body, int p_shape_idx) const override;
+
+ virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) override;
+
+ virtual int body_get_shape_count(RID p_body) const override;
+
+ virtual void body_remove_shape(RID p_body, int p_shape_idx) override;
+ virtual void body_clear_shapes(RID p_body) override;
+
+ virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) override;
+ virtual ObjectID body_get_object_instance_id(RID p_body) const override;
+
+ virtual void body_set_enable_continuous_collision_detection(RID p_body, bool p_enable) override;
+ virtual bool body_is_continuous_collision_detection_enabled(RID p_body) const override;
+
+ virtual void body_set_collision_layer(RID p_body, uint32_t p_layer) override;
+ virtual uint32_t body_get_collision_layer(RID p_body) const override;
+
+ virtual void body_set_collision_mask(RID p_body, uint32_t p_mask) override;
+ virtual uint32_t body_get_collision_mask(RID p_body) const override;
+
+ virtual void body_set_collision_priority(RID p_body, real_t p_priority) override;
+ virtual real_t body_get_collision_priority(RID p_body) const override;
+
+ virtual void body_set_user_flags(RID p_body, uint32_t p_flags) override;
+ virtual uint32_t body_get_user_flags(RID p_body) const override;
+
+ virtual void body_set_param(RID p_body, PhysicsServer3D::BodyParameter p_param, const Variant &p_value) override;
+ virtual Variant body_get_param(RID p_body, PhysicsServer3D::BodyParameter p_param) const override;
+
+ virtual void body_reset_mass_properties(RID p_body) override;
+
+ virtual void body_set_state(RID p_body, PhysicsServer3D::BodyState p_state, const Variant &p_value) override;
+ virtual Variant body_get_state(RID p_body, PhysicsServer3D::BodyState p_state) const override;
+
+ virtual void body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) override;
+ virtual void body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position) override;
+ virtual void body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) override;
+
+ virtual void body_apply_central_force(RID p_body, const Vector3 &p_force) override;
+ virtual void body_apply_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position) override;
+ virtual void body_apply_torque(RID p_body, const Vector3 &p_torque) override;
+
+ virtual void body_add_constant_central_force(RID p_body, const Vector3 &p_force) override;
+ virtual void body_add_constant_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position) override;
+ virtual void body_add_constant_torque(RID p_body, const Vector3 &p_torque) override;
+
+ virtual void body_set_constant_force(RID p_body, const Vector3 &p_force) override;
+ virtual Vector3 body_get_constant_force(RID p_body) const override;
+
+ virtual void body_set_constant_torque(RID p_body, const Vector3 &p_torque) override;
+ virtual Vector3 body_get_constant_torque(RID p_body) const override;
+
+ virtual void body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity) override;
+
+ virtual void body_set_axis_lock(RID p_body, PhysicsServer3D::BodyAxis p_axis, bool p_lock) override;
+ virtual bool body_is_axis_locked(RID p_body, PhysicsServer3D::BodyAxis p_axis) const override;
+
+ virtual void body_add_collision_exception(RID p_body, RID p_excepted_body) override;
+ virtual void body_remove_collision_exception(RID p_body, RID p_excepted_body) override;
+ virtual void body_get_collision_exceptions(RID p_body, List *p_exceptions) override;
+
+ virtual void body_set_max_contacts_reported(RID p_body, int p_amount) override;
+ virtual int body_get_max_contacts_reported(RID p_body) const override;
+
+ virtual void body_set_contacts_reported_depth_threshold(RID p_body, real_t p_threshold) override;
+ virtual real_t body_get_contacts_reported_depth_threshold(RID p_body) const override;
+
+ virtual void body_set_omit_force_integration(RID p_body, bool p_enable) override;
+ virtual bool body_is_omitting_force_integration(RID p_body) const override;
+
+ virtual void body_set_state_sync_callback(RID p_body, const Callable &p_callable) override;
+ virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_userdata) override;
+
+ virtual void body_set_ray_pickable(RID p_body, bool p_enable) override;
+
+ virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result) override;
+
+ virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override;
+
+ virtual RID soft_body_create() override;
+
+ virtual void soft_body_update_rendering_server(RID p_body, PhysicsServer3DRenderingServerHandler *p_rendering_server_handler) override;
+
+ virtual void soft_body_set_space(RID p_body, RID p_space) override;
+ virtual RID soft_body_get_space(RID p_body) const override;
+
+ virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) override;
+
+ virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) override;
+ virtual uint32_t soft_body_get_collision_layer(RID p_body) const override;
+
+ virtual void soft_body_set_collision_mask(RID p_body, uint32_t p_mask) override;
+ virtual uint32_t soft_body_get_collision_mask(RID p_body) const override;
+
+ virtual void soft_body_add_collision_exception(RID p_body, RID p_excepted_body) override;
+ virtual void soft_body_remove_collision_exception(RID p_body, RID p_excepted_body) override;
+ virtual void soft_body_get_collision_exceptions(RID p_body, List *p_exceptions) override;
+
+ virtual void soft_body_set_state(RID p_body, PhysicsServer3D::BodyState p_state, const Variant &p_value) override;
+ virtual Variant soft_body_get_state(RID p_body, PhysicsServer3D::BodyState p_state) const override;
+
+ virtual void soft_body_set_transform(RID p_body, const Transform3D &p_transform) override;
+
+ virtual void soft_body_set_simulation_precision(RID p_body, int p_precision) override;
+ virtual int soft_body_get_simulation_precision(RID p_body) const override;
+
+ virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) override;
+ virtual real_t soft_body_get_total_mass(RID p_body) const override;
+
+ virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_coefficient) override;
+ virtual real_t soft_body_get_linear_stiffness(RID p_body) const override;
+
+ virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_coefficient) override;
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body) const override;
+
+ virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_coefficient) override;
+ virtual real_t soft_body_get_damping_coefficient(RID p_body) const override;
+
+ virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_coefficient) override;
+ virtual real_t soft_body_get_drag_coefficient(RID p_body) const override;
+
+ virtual void soft_body_set_mesh(RID p_body, RID p_mesh) override;
+
+ virtual AABB soft_body_get_bounds(RID p_body) const override;
+
+ virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) override;
+
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) const override;
+
+ virtual void soft_body_remove_all_pinned_points(RID p_body) override;
+
+ virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) override;
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) const override;
+
+ virtual RID joint_create() override;
+ virtual void joint_clear(RID p_joint) override;
+
+ virtual void joint_make_pin(RID p_joint, RID p_body_a, const Vector3 &p_local_a, RID p_body_b, const Vector3 &p_local_b) override;
+
+ virtual void pin_joint_set_param(RID p_joint, PhysicsServer3D::PinJointParam p_param, real_t p_value) override;
+ virtual real_t pin_joint_get_param(RID p_joint, PhysicsServer3D::PinJointParam p_param) const override;
+
+ virtual void pin_joint_set_local_a(RID p_joint, const Vector3 &p_local_a) override;
+ virtual Vector3 pin_joint_get_local_a(RID p_joint) const override;
+
+ virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_local_b) override;
+ virtual Vector3 pin_joint_get_local_b(RID p_joint) const override;
+
+ virtual void joint_make_hinge(RID p_joint, RID p_body_a, const Transform3D &p_hinge_a, RID p_body_b, const Transform3D &p_hinge_b) override;
+
+ virtual void joint_make_hinge_simple(RID p_joint, RID p_body_a, const Vector3 &p_pivot_a, const Vector3 &p_axis_a, RID p_body_b, const Vector3 &p_pivot_b, const Vector3 &p_axis_b) override;
+
+ virtual void hinge_joint_set_param(RID p_joint, PhysicsServer3D::HingeJointParam p_param, real_t p_value) override;
+ virtual real_t hinge_joint_get_param(RID p_joint, PhysicsServer3D::HingeJointParam p_param) const override;
+
+ virtual void hinge_joint_set_flag(RID p_joint, PhysicsServer3D::HingeJointFlag p_flag, bool p_enabled) override;
+ virtual bool hinge_joint_get_flag(RID p_joint, PhysicsServer3D::HingeJointFlag p_flag) const override;
+
+ virtual void joint_make_slider(RID p_joint, RID p_body_a, const Transform3D &p_local_ref_a, RID p_body_b, const Transform3D &p_local_ref_b) override;
+
+ virtual void slider_joint_set_param(RID p_joint, PhysicsServer3D::SliderJointParam p_param, real_t p_value) override;
+ virtual real_t slider_joint_get_param(RID p_joint, PhysicsServer3D::SliderJointParam p_param) const override;
+
+ virtual void joint_make_cone_twist(RID p_joint, RID p_body_a, const Transform3D &p_local_ref_a, RID p_body_b, const Transform3D &p_local_ref_b) override;
+
+ virtual void cone_twist_joint_set_param(RID p_joint, PhysicsServer3D::ConeTwistJointParam p_param, real_t p_value) override;
+ virtual real_t cone_twist_joint_get_param(RID p_joint, PhysicsServer3D::ConeTwistJointParam p_param) const override;
+
+ virtual void joint_make_generic_6dof(RID p_joint, RID p_body_a, const Transform3D &p_local_ref_a, RID p_body_b, const Transform3D &p_local_ref_b) override;
+
+ virtual void generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisParam p_param, real_t p_value) override;
+ virtual real_t generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisParam p_param) const override;
+
+ virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag, bool p_enable) override;
+ virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag) const override;
+
+ virtual PhysicsServer3D::JointType joint_get_type(RID p_joint) const override;
+
+ virtual void joint_set_solver_priority(RID p_joint, int p_priority) override;
+ virtual int joint_get_solver_priority(RID p_joint) const override;
+
+ virtual void joint_disable_collisions_between_bodies(RID p_joint, bool p_disable) override;
+ virtual bool joint_is_disabled_collisions_between_bodies(RID p_joint) const override;
+
+ virtual void free(RID p_rid) override;
+
+ virtual void set_active(bool p_active) override;
+
+ virtual void init() override;
+ virtual void finish() override;
+
+ virtual void step(real_t p_step) override;
+
+ virtual void sync() override;
+ virtual void end_sync() override;
+
+ virtual void flush_queries() override;
+ virtual bool is_flushing_queries() const override;
+
+ virtual int get_process_info(PhysicsServer3D::ProcessInfo p_process_info) override;
+
+ bool is_active() const { return active; }
+
+ void free_space(JoltSpace3D *p_space);
+ void free_area(JoltArea3D *p_area);
+ void free_body(JoltBody3D *p_body);
+ void free_soft_body(JoltSoftBody3D *p_body);
+ void free_shape(JoltShape3D *p_shape);
+ void free_joint(JoltJoint3D *p_joint);
+
+ JoltSpace3D *get_space(RID p_rid) const { return space_owner.get_or_null(p_rid); }
+ JoltArea3D *get_area(RID p_rid) const { return area_owner.get_or_null(p_rid); }
+ JoltBody3D *get_body(RID p_rid) const { return body_owner.get_or_null(p_rid); }
+ JoltShape3D *get_shape(RID p_rid) const { return shape_owner.get_or_null(p_rid); }
+ JoltJoint3D *get_joint(RID p_rid) const { return joint_owner.get_or_null(p_rid); }
+
+#ifdef DEBUG_ENABLED
+ void dump_debug_snapshots(const String &p_dir);
+
+ void space_dump_debug_snapshot(RID p_space, const String &p_dir);
+#endif
+
+ bool joint_get_enabled(RID p_joint) const;
+ void joint_set_enabled(RID p_joint, bool p_enabled);
+
+ int joint_get_solver_velocity_iterations(RID p_joint);
+ void joint_set_solver_velocity_iterations(RID p_joint, int p_value);
+
+ int joint_get_solver_position_iterations(RID p_joint);
+ void joint_set_solver_position_iterations(RID p_joint, int p_value);
+
+ float pin_joint_get_applied_force(RID p_joint);
+
+ double hinge_joint_get_jolt_param(RID p_joint, HingeJointParamJolt p_param) const;
+ void hinge_joint_set_jolt_param(RID p_joint, HingeJointParamJolt p_param, double p_value);
+
+ bool hinge_joint_get_jolt_flag(RID p_joint, HingeJointFlagJolt p_flag) const;
+ void hinge_joint_set_jolt_flag(RID p_joint, HingeJointFlagJolt p_flag, bool p_enabled);
+
+ float hinge_joint_get_applied_force(RID p_joint);
+ float hinge_joint_get_applied_torque(RID p_joint);
+
+ double slider_joint_get_jolt_param(RID p_joint, SliderJointParamJolt p_param) const;
+ void slider_joint_set_jolt_param(RID p_joint, SliderJointParamJolt p_param, double p_value);
+
+ bool slider_joint_get_jolt_flag(RID p_joint, SliderJointFlagJolt p_flag) const;
+ void slider_joint_set_jolt_flag(RID p_joint, SliderJointFlagJolt p_flag, bool p_enabled);
+
+ float slider_joint_get_applied_force(RID p_joint);
+ float slider_joint_get_applied_torque(RID p_joint);
+
+ double cone_twist_joint_get_jolt_param(RID p_joint, ConeTwistJointParamJolt p_param) const;
+ void cone_twist_joint_set_jolt_param(RID p_joint, ConeTwistJointParamJolt p_param, double p_value);
+
+ bool cone_twist_joint_get_jolt_flag(RID p_joint, ConeTwistJointFlagJolt p_flag) const;
+ void cone_twist_joint_set_jolt_flag(RID p_joint, ConeTwistJointFlagJolt p_flag, bool p_enabled);
+
+ float cone_twist_joint_get_applied_force(RID p_joint);
+ float cone_twist_joint_get_applied_torque(RID p_joint);
+
+ double generic_6dof_joint_get_jolt_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParamJolt p_param) const;
+ void generic_6dof_joint_set_jolt_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParamJolt p_param, double p_value);
+
+ bool generic_6dof_joint_get_jolt_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlagJolt p_flag) const;
+ void generic_6dof_joint_set_jolt_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlagJolt p_flag, bool p_enabled);
+
+ float generic_6dof_joint_get_applied_force(RID p_joint);
+ float generic_6dof_joint_get_applied_torque(RID p_joint);
+};
+
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::HingeJointParamJolt)
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::HingeJointFlagJolt)
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::SliderJointParamJolt)
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::SliderJointFlagJolt)
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::ConeTwistJointParamJolt)
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::ConeTwistJointFlagJolt)
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::G6DOFJointAxisParamJolt)
+VARIANT_ENUM_CAST(JoltPhysicsServer3D::G6DOFJointAxisFlagJolt)
+
+#endif // JOLT_PHYSICS_SERVER_3D_H
diff --git a/modules/jolt_physics/jolt_project_settings.cpp b/modules/jolt_physics/jolt_project_settings.cpp
new file mode 100644
index 000000000000..5b530f23da99
--- /dev/null
+++ b/modules/jolt_physics/jolt_project_settings.cpp
@@ -0,0 +1,256 @@
+/**************************************************************************/
+/* jolt_project_settings.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_project_settings.h"
+
+#include "core/config/project_settings.h"
+#include "core/error/error_macros.h"
+#include "core/math/math_funcs.h"
+
+namespace {
+
+enum JoltJointWorldNode : int {
+ JOLT_JOINT_WORLD_NODE_A,
+ JOLT_JOINT_WORLD_NODE_B,
+};
+
+} // namespace
+
+void JoltProjectSettings::register_settings() {
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/simulation/velocity_steps", PROPERTY_HINT_RANGE, U"2,16,or_greater"), 10);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/simulation/position_steps", PROPERTY_HINT_RANGE, U"1,16,or_greater"), 2);
+ GLOBAL_DEF(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/simulation/use_enhanced_internal_edge_removal"), true);
+ GLOBAL_DEF(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/simulation/areas_detect_static_bodies"), false);
+ GLOBAL_DEF(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/simulation/generate_all_kinematic_contacts"), false);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/penetration_slop", PROPERTY_HINT_RANGE, U"0,1,0.00001,or_greater,suffix:m"), 0.02f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/speculative_contact_distance", PROPERTY_HINT_RANGE, U"0,1,0.00001,or_greater,suffix:m"), 0.02f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/baumgarte_stabilization_factor", PROPERTY_HINT_RANGE, U"0,1,0.01"), 0.2f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/soft_body_point_radius", PROPERTY_HINT_RANGE, U"0,1,0.001,or_greater,suffix:m"), 0.01f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/bounce_velocity_threshold", PROPERTY_HINT_NONE, U"suffix:m/s"), 1.0f);
+ GLOBAL_DEF(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/simulation/allow_sleep"), true);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/sleep_velocity_threshold", PROPERTY_HINT_NONE, U"suffix:m/s"), 0.03f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/sleep_time_threshold", PROPERTY_HINT_RANGE, U"0,5,0.01,or_greater,suffix:s"), 0.5f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/continuous_cd_movement_threshold", PROPERTY_HINT_RANGE, U"0,1,0.01"), 0.75f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/continuous_cd_max_penetration", PROPERTY_HINT_RANGE, U"0,1,0.01"), 0.25f);
+ GLOBAL_DEF(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/simulation/body_pair_contact_cache_enabled"), true);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/body_pair_contact_cache_distance_threshold", PROPERTY_HINT_RANGE, U"0,0.01,0.00001,or_greater,suffix:m"), 0.001f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/simulation/body_pair_contact_cache_angle_threshold", PROPERTY_HINT_RANGE, U"0,180,0.01,radians_as_degrees"), Math::deg_to_rad(2.0f));
+
+ GLOBAL_DEF(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/queries/use_enhanced_internal_edge_removal"), false);
+ GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/queries/enable_ray_cast_face_index"), false);
+
+ GLOBAL_DEF(PropertyInfo(Variant::BOOL, "physics/jolt_physics_3d/motion_queries/use_enhanced_internal_edge_removal"), true);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/motion_queries/recovery_iterations", PROPERTY_HINT_RANGE, U"1,8,or_greater"), 4);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/motion_queries/recovery_amount", PROPERTY_HINT_RANGE, U"0,1,0.01"), 0.4f);
+
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/collisions/collision_margin_fraction", PROPERTY_HINT_RANGE, U"0,1,0.00001"), 0.08f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/collisions/active_edge_threshold", PROPERTY_HINT_RANGE, U"0,90,0.01,radians_as_degrees"), Math::deg_to_rad(50.0f));
+
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/joints/world_node", PROPERTY_HINT_ENUM, U"Node A,Node B"), JOLT_JOINT_WORLD_NODE_A);
+
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/limits/temporary_memory_buffer_size", PROPERTY_HINT_RANGE, U"1,32,or_greater,suffix:MiB"), 32);
+ GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/limits/world_boundary_shape_size", PROPERTY_HINT_RANGE, U"2,2000,0.1,or_greater,suffix:m"), 2000.0f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/limits/max_linear_velocity", PROPERTY_HINT_RANGE, U"0,500,0.01,or_greater,suffix:m/s"), 500.0f);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/jolt_physics_3d/limits/max_angular_velocity", PROPERTY_HINT_RANGE, U"0,2700,0.01,or_greater,radians_as_degrees,suffix:°/s"), Math::deg_to_rad(2700.0f));
+ GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/limits/max_bodies", PROPERTY_HINT_RANGE, U"1,10240,or_greater"), 10240);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/limits/max_body_pairs", PROPERTY_HINT_RANGE, U"8,65536,or_greater"), 65536);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/jolt_physics_3d/limits/max_contact_constraints", PROPERTY_HINT_RANGE, U"8,20480,or_greater"), 20480);
+}
+
+int JoltProjectSettings::get_simulation_velocity_steps() {
+ static const int value = GLOBAL_GET("physics/jolt_physics_3d/simulation/velocity_steps");
+ return value;
+}
+
+int JoltProjectSettings::get_simulation_position_steps() {
+ static const int value = GLOBAL_GET("physics/jolt_physics_3d/simulation/position_steps");
+ return value;
+}
+
+bool JoltProjectSettings::use_enhanced_internal_edge_removal_for_bodies() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/simulation/use_enhanced_internal_edge_removal");
+ return value;
+}
+
+bool JoltProjectSettings::areas_detect_static_bodies() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/simulation/areas_detect_static_bodies");
+ return value;
+}
+
+bool JoltProjectSettings::should_generate_all_kinematic_contacts() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/simulation/generate_all_kinematic_contacts");
+ return value;
+}
+
+float JoltProjectSettings::get_penetration_slop() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/simulation/penetration_slop");
+ return value;
+}
+
+float JoltProjectSettings::get_speculative_contact_distance() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/simulation/speculative_contact_distance");
+ return value;
+}
+
+float JoltProjectSettings::get_baumgarte_stabilization_factor() {
+ static const float value = (float)GLOBAL_GET("physics/jolt_physics_3d/simulation/baumgarte_stabilization_factor");
+ return value;
+}
+
+float JoltProjectSettings::get_soft_body_point_radius() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/simulation/soft_body_point_radius");
+ return value;
+}
+
+float JoltProjectSettings::get_bounce_velocity_threshold() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/simulation/bounce_velocity_threshold");
+ return value;
+}
+
+bool JoltProjectSettings::is_sleep_allowed() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/simulation/allow_sleep");
+ return value;
+}
+
+float JoltProjectSettings::get_sleep_velocity_threshold() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/simulation/sleep_velocity_threshold");
+ return value;
+}
+
+float JoltProjectSettings::get_sleep_time_threshold() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/simulation/sleep_time_threshold");
+ return value;
+}
+
+float JoltProjectSettings::get_ccd_movement_threshold() {
+ static const float value = (float)GLOBAL_GET("physics/jolt_physics_3d/simulation/continuous_cd_movement_threshold");
+ return value;
+}
+
+float JoltProjectSettings::get_ccd_max_penetration() {
+ static const float value = (float)GLOBAL_GET("physics/jolt_physics_3d/simulation/continuous_cd_max_penetration");
+ return value;
+}
+
+bool JoltProjectSettings::is_body_pair_contact_cache_enabled() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/simulation/body_pair_contact_cache_enabled");
+ return value;
+}
+
+float JoltProjectSettings::get_body_pair_cache_distance_sq() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/simulation/body_pair_contact_cache_distance_threshold");
+ static const float squared_value = value * value;
+ return squared_value;
+}
+
+float JoltProjectSettings::get_body_pair_cache_angle_cos_div2() {
+ static const float value = Math::cos((float)GLOBAL_GET("physics/jolt_physics_3d/simulation/body_pair_contact_cache_angle_threshold") / 2.0f);
+ return value;
+}
+
+bool JoltProjectSettings::use_enhanced_internal_edge_removal_for_queries() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/queries/use_enhanced_internal_edge_removal");
+ return value;
+}
+
+bool JoltProjectSettings::enable_ray_cast_face_index() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/queries/enable_ray_cast_face_index");
+ return value;
+}
+
+bool JoltProjectSettings::use_enhanced_internal_edge_removal_for_motion_queries() {
+ static const bool value = GLOBAL_GET("physics/jolt_physics_3d/motion_queries/use_enhanced_internal_edge_removal");
+ return value;
+}
+
+int JoltProjectSettings::get_motion_query_recovery_iterations() {
+ static const int value = GLOBAL_GET("physics/jolt_physics_3d/motion_queries/recovery_iterations");
+ return value;
+}
+
+float JoltProjectSettings::get_motion_query_recovery_amount() {
+ static const float value = (float)GLOBAL_GET("physics/jolt_physics_3d/motion_queries/recovery_amount");
+ return value;
+}
+
+float JoltProjectSettings::get_collision_margin_fraction() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/collisions/collision_margin_fraction");
+ return value;
+}
+
+float JoltProjectSettings::get_active_edge_threshold() {
+ static const float value = Math::cos((float)GLOBAL_GET("physics/jolt_physics_3d/collisions/active_edge_threshold"));
+ return value;
+}
+
+bool JoltProjectSettings::use_joint_world_node_a() {
+ static const bool value = (int)GLOBAL_GET("physics/jolt_physics_3d/joints/world_node") == JOLT_JOINT_WORLD_NODE_A;
+ return value;
+}
+
+int JoltProjectSettings::get_temp_memory_mib() {
+ static const int value = GLOBAL_GET("physics/jolt_physics_3d/limits/temporary_memory_buffer_size");
+ return value;
+}
+
+int64_t JoltProjectSettings::get_temp_memory_b() {
+ static const int64_t value = get_temp_memory_mib() * 1024 * 1024;
+ return value;
+}
+
+float JoltProjectSettings::get_world_boundary_shape_size() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/limits/world_boundary_shape_size");
+ return value;
+}
+
+float JoltProjectSettings::get_max_linear_velocity() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/limits/max_linear_velocity");
+ return value;
+}
+
+float JoltProjectSettings::get_max_angular_velocity() {
+ static const float value = GLOBAL_GET("physics/jolt_physics_3d/limits/max_angular_velocity");
+ return value;
+}
+
+int JoltProjectSettings::get_max_bodies() {
+ static const int value = GLOBAL_GET("physics/jolt_physics_3d/limits/max_bodies");
+ return value;
+}
+
+int JoltProjectSettings::get_max_pairs() {
+ static const int value = GLOBAL_GET("physics/jolt_physics_3d/limits/max_body_pairs");
+ return value;
+}
+
+int JoltProjectSettings::get_max_contact_constraints() {
+ static const int value = GLOBAL_GET("physics/jolt_physics_3d/limits/max_contact_constraints");
+ return value;
+}
diff --git a/modules/jolt_physics/jolt_project_settings.h b/modules/jolt_physics/jolt_project_settings.h
new file mode 100644
index 000000000000..48710d41033c
--- /dev/null
+++ b/modules/jolt_physics/jolt_project_settings.h
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* jolt_project_settings.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_PROJECT_SETTINGS_H
+#define JOLT_PROJECT_SETTINGS_H
+
+#include
+
+class JoltProjectSettings {
+public:
+ static void register_settings();
+
+ static int get_simulation_velocity_steps();
+ static int get_simulation_position_steps();
+ static bool use_enhanced_internal_edge_removal_for_bodies();
+ static bool areas_detect_static_bodies();
+ static bool should_generate_all_kinematic_contacts();
+ static float get_penetration_slop();
+ static float get_speculative_contact_distance();
+ static float get_baumgarte_stabilization_factor();
+ static float get_soft_body_point_radius();
+ static float get_bounce_velocity_threshold();
+ static bool is_sleep_allowed();
+ static float get_sleep_velocity_threshold();
+ static float get_sleep_time_threshold();
+ static float get_ccd_movement_threshold();
+ static float get_ccd_max_penetration();
+ static bool is_body_pair_contact_cache_enabled();
+ static float get_body_pair_cache_distance_sq();
+ static float get_body_pair_cache_angle_cos_div2();
+
+ static bool use_enhanced_internal_edge_removal_for_queries();
+ static bool enable_ray_cast_face_index();
+
+ static bool use_enhanced_internal_edge_removal_for_motion_queries();
+ static int get_motion_query_recovery_iterations();
+ static float get_motion_query_recovery_amount();
+
+ static float get_collision_margin_fraction();
+ static float get_active_edge_threshold();
+
+ static bool use_joint_world_node_a();
+
+ static int get_temp_memory_mib();
+ static int64_t get_temp_memory_b();
+ static float get_world_boundary_shape_size();
+ static float get_max_linear_velocity();
+ static float get_max_angular_velocity();
+ static int get_max_bodies();
+ static int get_max_pairs();
+ static int get_max_contact_constraints();
+};
+
+#endif // JOLT_PROJECT_SETTINGS_H
diff --git a/modules/jolt_physics/misc/jolt_stream_wrappers.h b/modules/jolt_physics/misc/jolt_stream_wrappers.h
new file mode 100644
index 000000000000..0200a4515730
--- /dev/null
+++ b/modules/jolt_physics/misc/jolt_stream_wrappers.h
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* jolt_stream_wrappers.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_STREAM_WRAPPERS_H
+#define JOLT_STREAM_WRAPPERS_H
+
+#ifdef DEBUG_ENABLED
+
+#include "core/io/file_access.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Core/StreamIn.h"
+#include "Jolt/Core/StreamOut.h"
+
+class JoltStreamOutputWrapper final : public JPH::StreamOut {
+ Ref file_access;
+
+public:
+ explicit JoltStreamOutputWrapper(const Ref &p_file_access) :
+ file_access(p_file_access) {}
+
+ virtual void WriteBytes(const void *p_data, size_t p_bytes) override {
+ file_access->store_buffer(static_cast(p_data), static_cast(p_bytes));
+ }
+
+ virtual bool IsFailed() const override {
+ return file_access->get_error() != OK;
+ }
+};
+
+class JoltStreamInputWrapper final : public JPH::StreamIn {
+ Ref file_access;
+
+public:
+ explicit JoltStreamInputWrapper(const Ref &p_file_access) :
+ file_access(p_file_access) {}
+
+ virtual void ReadBytes(void *p_data, size_t p_bytes) override {
+ file_access->get_buffer(static_cast(p_data), static_cast(p_bytes));
+ }
+
+ virtual bool IsEOF() const override {
+ return file_access->eof_reached();
+ }
+
+ virtual bool IsFailed() const override {
+ return file_access->get_error() != OK;
+ }
+};
+
+#endif
+
+#endif // JOLT_STREAM_WRAPPERS_H
diff --git a/modules/jolt_physics/misc/jolt_type_conversions.h b/modules/jolt_physics/misc/jolt_type_conversions.h
new file mode 100644
index 000000000000..f735d6e06bc2
--- /dev/null
+++ b/modules/jolt_physics/misc/jolt_type_conversions.h
@@ -0,0 +1,147 @@
+/**************************************************************************/
+/* jolt_type_conversions.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_TYPE_CONVERSIONS_H
+#define JOLT_TYPE_CONVERSIONS_H
+
+#include "core/math/aabb.h"
+#include "core/math/color.h"
+#include "core/math/plane.h"
+#include "core/math/quaternion.h"
+#include "core/math/transform_3d.h"
+#include "core/string/ustring.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Core/Color.h"
+#include "Jolt/Geometry/AABox.h"
+#include "Jolt/Geometry/Plane.h"
+#include "Jolt/Math/Mat44.h"
+#include "Jolt/Math/Quat.h"
+#include "Jolt/Math/Vec3.h"
+
+_FORCE_INLINE_ Vector3 to_godot(const JPH::Vec3 &p_vec) {
+ return Vector3((real_t)p_vec.GetX(), (real_t)p_vec.GetY(), (real_t)p_vec.GetZ());
+}
+
+_FORCE_INLINE_ Vector3 to_godot(const JPH::DVec3 &p_vec) {
+ return Vector3((real_t)p_vec.GetX(), (real_t)p_vec.GetY(), (real_t)p_vec.GetZ());
+}
+
+_FORCE_INLINE_ Basis to_godot(const JPH::Quat &p_quat) {
+ return Basis(Quaternion(p_quat.GetX(), p_quat.GetY(), p_quat.GetZ(), p_quat.GetW()));
+}
+
+_FORCE_INLINE_ Transform3D to_godot(const JPH::Mat44 &p_mat) {
+ return Transform3D(
+ Vector3(p_mat(0, 0), p_mat(1, 0), p_mat(2, 0)),
+ Vector3(p_mat(0, 1), p_mat(1, 1), p_mat(2, 1)),
+ Vector3(p_mat(0, 2), p_mat(1, 2), p_mat(2, 2)),
+ Vector3(p_mat(0, 3), p_mat(1, 3), p_mat(2, 3)));
+}
+
+_FORCE_INLINE_ Color to_godot(const JPH::Color &p_color) {
+ const float r = (float)p_color.r;
+ const float g = (float)p_color.g;
+ const float b = (float)p_color.b;
+ const float a = (float)p_color.a;
+
+ return Color(
+ r == 0.0f ? 0.0f : 255.0f / r,
+ g == 0.0f ? 0.0f : 255.0f / g,
+ b == 0.0f ? 0.0f : 255.0f / b,
+ a == 0.0f ? 0.0f : 255.0f / a);
+}
+
+_FORCE_INLINE_ String to_godot(const JPH::String &p_str) {
+ return String::utf8(p_str.c_str(), (int)p_str.length());
+}
+
+_FORCE_INLINE_ AABB to_godot(const JPH::AABox &p_aabb) {
+ return AABB(to_godot(p_aabb.mMin), to_godot(p_aabb.mMax - p_aabb.mMin));
+}
+
+_FORCE_INLINE_ Plane to_godot(const JPH::Plane &p_plane) {
+ return Plane(to_godot(p_plane.GetNormal()), (real_t)p_plane.GetConstant());
+}
+
+_FORCE_INLINE_ JPH::Vec3 to_jolt(const Vector3 &p_vec) {
+ return JPH::Vec3((float)p_vec.x, (float)p_vec.y, (float)p_vec.z);
+}
+
+_FORCE_INLINE_ JPH::Quat to_jolt(const Basis &p_basis) {
+ const Quaternion quat = p_basis.get_quaternion().normalized();
+ return JPH::Quat((float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w);
+}
+
+_FORCE_INLINE_ JPH::Mat44 to_jolt(const Transform3D &p_transform) {
+ const Basis &b = p_transform.basis;
+ const Vector3 &o = p_transform.origin;
+
+ return JPH::Mat44(
+ JPH::Vec4(b[0][0], b[1][0], b[2][0], 0.0f),
+ JPH::Vec4(b[0][1], b[1][1], b[2][1], 0.0f),
+ JPH::Vec4(b[0][2], b[1][2], b[2][2], 0.0f),
+ JPH::Vec3(o.x, o.y, o.z));
+}
+
+_FORCE_INLINE_ JPH::Color to_jolt(const Color &p_color) {
+ return JPH::Color((JPH::uint32)p_color.to_abgr32());
+}
+
+_FORCE_INLINE_ JPH::String to_jolt(const String &p_str) {
+ const CharString str_utf8 = p_str.utf8();
+ return JPH::String(str_utf8.get_data(), (size_t)str_utf8.length());
+}
+
+_FORCE_INLINE_ JPH::AABox to_jolt(const AABB &p_aabb) {
+ return JPH::AABox(to_jolt(p_aabb.position), to_jolt(p_aabb.position + p_aabb.size));
+}
+
+_FORCE_INLINE_ JPH::Plane to_jolt(const Plane &p_plane) {
+ return JPH::Plane(to_jolt(p_plane.normal), (float)p_plane.d);
+}
+
+_FORCE_INLINE_ JPH::RVec3 to_jolt_r(const Vector3 &p_vec) {
+ return JPH::RVec3(p_vec.x, p_vec.y, p_vec.z);
+}
+
+_FORCE_INLINE_ JPH::RMat44 to_jolt_r(const Transform3D &p_transform) {
+ const Basis &b = p_transform.basis;
+ const Vector3 &o = p_transform.origin;
+
+ return JPH::RMat44(
+ JPH::Vec4(b[0][0], b[1][0], b[2][0], 0.0f),
+ JPH::Vec4(b[0][1], b[1][1], b[2][1], 0.0f),
+ JPH::Vec4(b[0][2], b[1][2], b[2][2], 0.0f),
+ JPH::RVec3(o.x, o.y, o.z));
+}
+
+#endif // JOLT_TYPE_CONVERSIONS_H
diff --git a/modules/jolt_physics/objects/jolt_area_3d.cpp b/modules/jolt_physics/objects/jolt_area_3d.cpp
new file mode 100644
index 000000000000..84313477b11c
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_area_3d.cpp
@@ -0,0 +1,655 @@
+/**************************************************************************/
+/* jolt_area_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_area_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+#include "../shapes/jolt_shape_3d.h"
+#include "../spaces/jolt_broad_phase_layer.h"
+#include "../spaces/jolt_space_3d.h"
+#include "jolt_body_3d.h"
+#include "jolt_group_filter.h"
+#include "jolt_soft_body_3d.h"
+
+#include "core/error/error_macros.h"
+
+namespace {
+
+constexpr double DEFAULT_WIND_FORCE_MAGNITUDE = 0.0;
+constexpr double DEFAULT_WIND_ATTENUATION_FACTOR = 0.0;
+
+const Vector3 DEFAULT_WIND_SOURCE = Vector3();
+const Vector3 DEFAULT_WIND_DIRECTION = Vector3();
+
+} // namespace
+
+JPH::BroadPhaseLayer JoltArea3D::_get_broad_phase_layer() const {
+ return monitorable ? JoltBroadPhaseLayer::AREA_DETECTABLE : JoltBroadPhaseLayer::AREA_UNDETECTABLE;
+}
+
+JPH::ObjectLayer JoltArea3D::_get_object_layer() const {
+ ERR_FAIL_NULL_V(space, 0);
+
+ return space->map_to_object_layer(_get_broad_phase_layer(), collision_layer, collision_mask);
+}
+
+void JoltArea3D::_add_to_space() {
+ jolt_shape = build_shape();
+
+ JPH::CollisionGroup::GroupID group_id = 0;
+ JPH::CollisionGroup::SubGroupID sub_group_id = 0;
+ JoltGroupFilter::encode_object(this, group_id, sub_group_id);
+
+ jolt_settings->mUserData = reinterpret_cast(this);
+ jolt_settings->mObjectLayer = _get_object_layer();
+ jolt_settings->mCollisionGroup = JPH::CollisionGroup(nullptr, group_id, sub_group_id);
+ jolt_settings->mMotionType = _get_motion_type();
+ jolt_settings->mIsSensor = true;
+ jolt_settings->mUseManifoldReduction = false;
+
+ if (JoltProjectSettings::areas_detect_static_bodies()) {
+ jolt_settings->mCollideKinematicVsNonDynamic = true;
+ }
+
+ jolt_settings->SetShape(build_shape());
+
+ const JPH::BodyID new_jolt_id = space->add_rigid_body(*this, *jolt_settings);
+ if (!new_jolt_id.IsInvalid()) {
+ jolt_id = new_jolt_id;
+ }
+
+ delete jolt_settings;
+ jolt_settings = nullptr;
+}
+
+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 JoltReadableBody3D other_jolt_body = space->read_body(p_body_id);
+ const JoltShapedObject3D *other_object = other_jolt_body.as_shaped();
+ ERR_FAIL_NULL(other_object);
+
+ p_overlap.rid = other_object->get_rid();
+ p_overlap.instance_id = other_object->get_instance_id();
+
+ ShapeIndexPair &shape_indices = p_overlap.shape_pairs[{ p_other_shape_id, p_self_shape_id }];
+
+ shape_indices.other = other_object->find_shape_index(p_other_shape_id);
+ shape_indices.self = find_shape_index(p_self_shape_id);
+
+ p_overlap.pending_added.push_back(shape_indices);
+}
+
+bool JoltArea3D::_remove_shape_pair(Overlap &p_overlap, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
+ HashMap::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;
+ }
+
+ p_overlap.pending_removed.push_back(shape_pair->value);
+ p_overlap.shape_pairs.remove(shape_pair);
+
+ return true;
+}
+
+void JoltArea3D::_flush_events(OverlapsById &p_objects, const Callable &p_callback) {
+ for (OverlapsById::Iterator E = p_objects.begin(); E;) {
+ Overlap &overlap = E->value;
+
+ if (p_callback.is_valid()) {
+ for (ShapeIndexPair &shape_indices : overlap.pending_removed) {
+ _report_event(p_callback, PhysicsServer3D::AREA_BODY_REMOVED, overlap.rid, overlap.instance_id, shape_indices.other, shape_indices.self);
+ }
+
+ for (ShapeIndexPair &shape_indices : overlap.pending_added) {
+ _report_event(p_callback, PhysicsServer3D::AREA_BODY_ADDED, overlap.rid, overlap.instance_id, shape_indices.other, shape_indices.self);
+ }
+ }
+
+ overlap.pending_removed.clear();
+ overlap.pending_added.clear();
+
+ OverlapsById::Iterator next = E;
+ ++next;
+
+ if (overlap.shape_pairs.is_empty()) {
+ p_objects.remove(E);
+ }
+
+ E = next;
+ }
+}
+
+void JoltArea3D::_report_event(const Callable &p_callback, PhysicsServer3D::AreaBodyStatus p_status, const RID &p_other_rid, ObjectID p_other_instance_id, int p_other_shape_index, int p_self_shape_index) const {
+ ERR_FAIL_COND(!p_callback.is_valid());
+
+ static thread_local Array arguments = []() {
+ Array array;
+ array.resize(5);
+ return array;
+ }();
+
+ arguments[0] = p_status;
+ arguments[1] = p_other_rid;
+ arguments[2] = p_other_instance_id;
+ arguments[3] = p_other_shape_index;
+ arguments[4] = p_self_shape_index;
+
+ p_callback.callv(arguments);
+}
+
+void JoltArea3D::_notify_body_entered(const JPH::BodyID &p_body_id) {
+ const JoltReadableBody3D jolt_body = space->read_body(p_body_id);
+
+ JoltBody3D *body = jolt_body.as_body();
+ if (unlikely(body == nullptr)) {
+ return;
+ }
+
+ body->add_area(this);
+}
+
+void JoltArea3D::_notify_body_exited(const JPH::BodyID &p_body_id) {
+ const JoltReadableBody3D jolt_body = space->read_body(p_body_id);
+
+ JoltBody3D *body = jolt_body.as_body();
+ if (unlikely(body == nullptr)) {
+ return;
+ }
+
+ body->remove_area(this);
+}
+
+void JoltArea3D::_force_bodies_entered() {
+ for (auto &[id, body] : bodies_by_id) {
+ for (const auto &[id_pair, index_pair] : body.shape_pairs) {
+ body.pending_removed.erase(index_pair);
+ body.pending_added.push_back(index_pair);
+ }
+ }
+}
+
+void JoltArea3D::_force_bodies_exited(bool p_remove) {
+ for (auto &[id, body] : bodies_by_id) {
+ for (const auto &[id_pair, index_pair] : body.shape_pairs) {
+ body.pending_added.erase(index_pair);
+ body.pending_removed.push_back(index_pair);
+ }
+
+ if (p_remove) {
+ body.shape_pairs.clear();
+ _notify_body_exited(id);
+ }
+ }
+}
+
+void JoltArea3D::_force_areas_entered() {
+ for (auto &[id, area] : areas_by_id) {
+ for (const auto &[id_pair, index_pair] : area.shape_pairs) {
+ area.pending_removed.erase(index_pair);
+ area.pending_added.push_back(index_pair);
+ }
+ }
+}
+
+void JoltArea3D::_force_areas_exited(bool p_remove) {
+ for (auto &[id, area] : areas_by_id) {
+ for (const auto &[id_pair, index_pair] : area.shape_pairs) {
+ area.pending_added.erase(index_pair);
+ area.pending_removed.push_back(index_pair);
+ }
+
+ if (p_remove) {
+ area.shape_pairs.clear();
+ }
+ }
+}
+
+void JoltArea3D::_update_group_filter() {
+ if (!in_space()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ 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();
+
+ if (space != nullptr) {
+ // Ideally we would rely on our contact listener to report all the exits when we move
+ // between (or out of) spaces, but because our Jolt body is going to be destroyed when we
+ // leave this space the contact listener won't be able to retrieve the corresponding area
+ // and as such cannot report any exits, so we're forced to do it manually instead.
+ _force_bodies_exited(true);
+ _force_areas_exited(true);
+ }
+}
+
+void JoltArea3D::_space_changed() {
+ JoltShapedObject3D::_space_changed();
+
+ _update_group_filter();
+ _update_default_gravity();
+}
+
+void JoltArea3D::_body_monitoring_changed() {
+ if (has_body_monitor_callback()) {
+ _force_bodies_entered();
+ } else {
+ _force_bodies_exited(false);
+ }
+}
+
+void JoltArea3D::_area_monitoring_changed() {
+ if (has_area_monitor_callback()) {
+ _force_areas_entered();
+ } else {
+ _force_areas_exited(false);
+ }
+}
+
+void JoltArea3D::_monitorable_changed() {
+ _update_object_layer();
+}
+
+void JoltArea3D::_gravity_changed() {
+ _update_default_gravity();
+}
+
+JoltArea3D::JoltArea3D() :
+ JoltShapedObject3D(OBJECT_TYPE_AREA) {
+}
+
+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()));
+
+ const Vector3 new_scale = p_transform.basis.get_scale();
+
+ // Ideally we would do an exact comparison here, but due to floating-point precision this would be invalidated very often.
+ if (!scale.is_equal_approx(new_scale)) {
+ scale = new_scale;
+ _shapes_changed();
+ }
+
+ p_transform.basis.orthonormalize();
+
+ if (!in_space()) {
+ jolt_settings->mPosition = to_jolt_r(p_transform.origin);
+ jolt_settings->mRotation = to_jolt(p_transform.basis);
+ } else {
+ space->get_body_iface().SetPositionAndRotation(jolt_id, to_jolt_r(p_transform.origin), to_jolt(p_transform.basis), JPH::EActivation::DontActivate);
+ }
+}
+
+Variant JoltArea3D::get_param(PhysicsServer3D::AreaParameter p_param) const {
+ switch (p_param) {
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_OVERRIDE_MODE: {
+ return get_gravity_mode();
+ }
+ case PhysicsServer3D::AREA_PARAM_GRAVITY: {
+ return get_gravity();
+ }
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR: {
+ return get_gravity_vector();
+ }
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT: {
+ return is_point_gravity();
+ }
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: {
+ return get_point_gravity_distance();
+ }
+ case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: {
+ return get_linear_damp_mode();
+ }
+ case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP: {
+ return get_linear_damp();
+ }
+ case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE: {
+ return get_angular_damp_mode();
+ }
+ case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP: {
+ return get_angular_damp();
+ }
+ case PhysicsServer3D::AREA_PARAM_PRIORITY: {
+ return get_priority();
+ }
+ case PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE: {
+ return DEFAULT_WIND_FORCE_MAGNITUDE;
+ }
+ case PhysicsServer3D::AREA_PARAM_WIND_SOURCE: {
+ return DEFAULT_WIND_SOURCE;
+ }
+ case PhysicsServer3D::AREA_PARAM_WIND_DIRECTION: {
+ return DEFAULT_WIND_DIRECTION;
+ }
+ case PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR: {
+ return DEFAULT_WIND_ATTENUATION_FACTOR;
+ }
+ default: {
+ ERR_FAIL_V_MSG(Variant(), vformat("Unhandled area parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltArea3D::set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value) {
+ switch (p_param) {
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_OVERRIDE_MODE: {
+ set_gravity_mode((OverrideMode)(int)p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_GRAVITY: {
+ set_gravity(p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR: {
+ set_gravity_vector(p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT: {
+ set_point_gravity(p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: {
+ set_point_gravity_distance(p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: {
+ set_linear_damp_mode((OverrideMode)(int)p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP: {
+ set_area_linear_damp(p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE: {
+ set_angular_damp_mode((OverrideMode)(int)p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP: {
+ set_area_angular_damp(p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_PRIORITY: {
+ set_priority(p_value);
+ } break;
+ case PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE: {
+ if (!Math::is_equal_approx((double)p_value, DEFAULT_WIND_FORCE_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()));
+ }
+ } break;
+ case PhysicsServer3D::AREA_PARAM_WIND_SOURCE: {
+ if (!((Vector3)p_value).is_equal_approx(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()));
+ }
+ } break;
+ case PhysicsServer3D::AREA_PARAM_WIND_DIRECTION: {
+ if (!((Vector3)p_value).is_equal_approx(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()));
+ }
+ } break;
+ case PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR: {
+ if (!Math::is_equal_approx((double)p_value, DEFAULT_WIND_ATTENUATION_FACTOR)) {
+ 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()));
+ }
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled area parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+void JoltArea3D::set_body_monitor_callback(const Callable &p_callback) {
+ if (p_callback == body_monitor_callback) {
+ return;
+ }
+
+ body_monitor_callback = p_callback;
+
+ _body_monitoring_changed();
+}
+
+void JoltArea3D::set_area_monitor_callback(const Callable &p_callback) {
+ if (p_callback == area_monitor_callback) {
+ return;
+ }
+
+ area_monitor_callback = p_callback;
+
+ _area_monitoring_changed();
+}
+
+void JoltArea3D::set_monitorable(bool p_monitorable) {
+ if (p_monitorable == monitorable) {
+ return;
+ }
+
+ monitorable = p_monitorable;
+
+ _monitorable_changed();
+}
+
+bool JoltArea3D::can_monitor(const JoltBody3D &p_other) const {
+ return (collision_mask & p_other.get_collision_layer()) != 0;
+}
+
+bool JoltArea3D::can_monitor(const JoltSoftBody3D &p_other) const {
+ return false;
+}
+
+bool JoltArea3D::can_monitor(const JoltArea3D &p_other) const {
+ return p_other.is_monitorable() && (collision_mask & p_other.get_collision_layer()) != 0;
+}
+
+bool JoltArea3D::can_interact_with(const JoltBody3D &p_other) const {
+ return can_monitor(p_other);
+}
+
+bool JoltArea3D::can_interact_with(const JoltSoftBody3D &p_other) const {
+ return false;
+}
+
+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 { 0.0f, 0.0f, 0.0f };
+}
+
+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;
+ }
+
+ const Vector3 point = get_transform_scaled().xform(gravity_vector);
+ const Vector3 to_point = point - p_position;
+ const real_t to_point_dist_sq = MAX(to_point.length_squared(), (real_t)CMP_EPSILON);
+ const Vector3 to_point_dir = to_point / Math::sqrt(to_point_dist_sq);
+
+ if (point_gravity_distance == 0.0f) {
+ return to_point_dir * gravity;
+ }
+
+ const float gravity_dist_sq = point_gravity_distance * point_gravity_distance;
+
+ return to_point_dir * (gravity * gravity_dist_sq / to_point_dist_sq);
+}
+
+void JoltArea3D::body_shape_entered(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
+ Overlap &overlap = bodies_by_id[p_body_id];
+
+ if (overlap.shape_pairs.is_empty()) {
+ _notify_body_entered(p_body_id);
+ }
+
+ _add_shape_pair(overlap, p_body_id, p_other_shape_id, p_self_shape_id);
+}
+
+bool JoltArea3D::body_shape_exited(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
+ Overlap *overlap = bodies_by_id.getptr(p_body_id);
+
+ if (overlap == nullptr) {
+ return false;
+ }
+
+ if (!_remove_shape_pair(*overlap, p_other_shape_id, p_self_shape_id)) {
+ return false;
+ }
+
+ if (overlap->shape_pairs.is_empty()) {
+ _notify_body_exited(p_body_id);
+ }
+
+ return true;
+}
+
+void JoltArea3D::area_shape_entered(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
+ _add_shape_pair(areas_by_id[p_body_id], p_body_id, p_other_shape_id, p_self_shape_id);
+}
+
+bool JoltArea3D::area_shape_exited(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
+ Overlap *overlap = areas_by_id.getptr(p_body_id);
+
+ if (overlap == nullptr) {
+ return false;
+ }
+
+ return _remove_shape_pair(*overlap, p_other_shape_id, p_self_shape_id);
+}
+
+bool JoltArea3D::shape_exited(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
+ return body_shape_exited(p_body_id, p_other_shape_id, p_self_shape_id) || area_shape_exited(p_body_id, p_other_shape_id, p_self_shape_id);
+}
+
+void JoltArea3D::body_exited(const JPH::BodyID &p_body_id, bool p_notify) {
+ Overlap *overlap = bodies_by_id.getptr(p_body_id);
+ if (unlikely(overlap == nullptr)) {
+ return;
+ }
+
+ if (unlikely(overlap->shape_pairs.is_empty())) {
+ return;
+ }
+
+ for (auto &[id_pair, index_pair] : overlap->shape_pairs) {
+ overlap->pending_added.erase(index_pair);
+ overlap->pending_removed.push_back(index_pair);
+ }
+
+ overlap->shape_pairs.clear();
+
+ if (p_notify) {
+ _notify_body_exited(p_body_id);
+ }
+}
+
+void JoltArea3D::area_exited(const JPH::BodyID &p_body_id) {
+ Overlap *overlap = areas_by_id.getptr(p_body_id);
+ if (unlikely(overlap == nullptr)) {
+ return;
+ }
+
+ if (unlikely(overlap->shape_pairs.is_empty())) {
+ return;
+ }
+
+ for (const auto &[id_pair, index_pair] : overlap->shape_pairs) {
+ overlap->pending_added.erase(index_pair);
+ overlap->pending_removed.push_back(index_pair);
+ }
+
+ overlap->shape_pairs.clear();
+}
+
+void JoltArea3D::call_queries(JPH::Body &p_jolt_body) {
+ _flush_events(bodies_by_id, body_monitor_callback);
+ _flush_events(areas_by_id, area_monitor_callback);
+}
diff --git a/modules/jolt_physics/objects/jolt_area_3d.h b/modules/jolt_physics/objects/jolt_area_3d.h
new file mode 100644
index 000000000000..2c8a67b4c2aa
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_area_3d.h
@@ -0,0 +1,228 @@
+/**************************************************************************/
+/* jolt_area_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_AREA_3D_H
+#define JOLT_AREA_3D_H
+
+#include "jolt_shaped_object_3d.h"
+
+#include "core/templates/hashfuncs.h"
+#include "core/variant/callable.h"
+#include "servers/physics_server_3d.h"
+
+class JoltBody3D;
+class JoltSoftBody3D;
+
+class JoltArea3D final : public JoltShapedObject3D {
+public:
+ typedef PhysicsServer3D::AreaSpaceOverrideMode OverrideMode;
+
+private:
+ struct BodyIDHasher {
+ static uint32_t hash(const JPH::BodyID &p_id) { return hash_fmix32(p_id.GetIndexAndSequenceNumber()); }
+ };
+
+ struct ShapeIDPair {
+ JPH::SubShapeID other;
+ JPH::SubShapeID self;
+
+ ShapeIDPair(JPH::SubShapeID p_other, JPH::SubShapeID p_self) :
+ other(p_other), self(p_self) {}
+
+ static uint32_t hash(const ShapeIDPair &p_pair) {
+ uint32_t hash = hash_murmur3_one_32(p_pair.other.GetValue());
+ hash = hash_murmur3_one_32(p_pair.self.GetValue(), hash);
+ return hash_fmix32(hash);
+ }
+
+ friend bool operator==(const ShapeIDPair &p_lhs, const ShapeIDPair &p_rhs) {
+ return (p_lhs.other == p_rhs.other) && (p_lhs.self == p_rhs.self);
+ }
+ };
+
+ struct ShapeIndexPair {
+ int other = -1;
+ int self = -1;
+
+ ShapeIndexPair() = default;
+
+ ShapeIndexPair(int p_other, int p_self) :
+ other(p_other), self(p_self) {}
+
+ friend bool operator==(const ShapeIndexPair &p_lhs, const ShapeIndexPair &p_rhs) {
+ return (p_lhs.other == p_rhs.other) && (p_lhs.self == p_rhs.self);
+ }
+ };
+
+ struct Overlap {
+ HashMap shape_pairs;
+ LocalVector pending_added;
+ LocalVector pending_removed;
+ RID rid;
+ ObjectID instance_id;
+ };
+
+ typedef HashMap OverlapsById;
+
+ OverlapsById bodies_by_id;
+ OverlapsById areas_by_id;
+
+ Vector3 gravity_vector = Vector3(0, -1, 0);
+
+ Callable body_monitor_callback;
+ Callable area_monitor_callback;
+
+ float priority = 0.0f;
+ float gravity = 9.8f;
+ float point_gravity_distance = 0.0f;
+ float linear_damp = 0.1f;
+ float angular_damp = 0.1f;
+
+ OverrideMode gravity_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
+ OverrideMode linear_damp_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
+ OverrideMode angular_damp_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
+
+ bool monitorable = false;
+ bool point_gravity = false;
+
+ virtual JPH::BroadPhaseLayer _get_broad_phase_layer() const override;
+ virtual JPH::ObjectLayer _get_object_layer() const override;
+
+ virtual JPH::EMotionType _get_motion_type() const override { return JPH::EMotionType::Kinematic; }
+
+ virtual void _add_to_space() override;
+
+ void _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);
+ bool _remove_shape_pair(Overlap &p_overlap, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
+
+ void _flush_events(OverlapsById &p_objects, const Callable &p_callback);
+
+ void _report_event(const Callable &p_callback, PhysicsServer3D::AreaBodyStatus p_status, const RID &p_other_rid, ObjectID p_other_instance_id, int p_other_shape_index, int p_self_shape_index) const;
+
+ void _notify_body_entered(const JPH::BodyID &p_body_id);
+ void _notify_body_exited(const JPH::BodyID &p_body_id);
+
+ void _force_bodies_entered();
+ void _force_bodies_exited(bool p_remove);
+
+ void _force_areas_entered();
+ void _force_areas_exited(bool p_remove);
+
+ void _update_group_filter();
+ void _update_default_gravity();
+
+ virtual void _space_changing() override;
+ virtual void _space_changed() override;
+ 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;
+ void set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value);
+
+ bool has_body_monitor_callback() const { return body_monitor_callback.is_valid(); }
+ void set_body_monitor_callback(const Callable &p_callback);
+
+ bool has_area_monitor_callback() const { return area_monitor_callback.is_valid(); }
+ void set_area_monitor_callback(const Callable &p_callback);
+
+ bool is_monitorable() const { return monitorable; }
+ void set_monitorable(bool p_monitorable);
+
+ bool can_monitor(const JoltBody3D &p_other) const;
+ bool can_monitor(const JoltSoftBody3D &p_other) const;
+ bool can_monitor(const JoltArea3D &p_other) const;
+
+ 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;
+
+ virtual Vector3 get_velocity_at_position(const Vector3 &p_position) const override;
+
+ virtual bool reports_contacts() const override { return false; }
+
+ bool is_point_gravity() const { return point_gravity; }
+ void set_point_gravity(bool 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);
+
+ float get_point_gravity_distance() const { return point_gravity_distance; }
+ void set_point_gravity_distance(float p_distance);
+
+ float get_linear_damp() const { return linear_damp; }
+ void set_area_linear_damp(float p_damp) { linear_damp = p_damp; }
+
+ float get_angular_damp() const { return angular_damp; }
+ 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);
+
+ OverrideMode get_linear_damp_mode() const { return linear_damp_mode; }
+ void set_linear_damp_mode(OverrideMode p_mode) { linear_damp_mode = p_mode; }
+
+ OverrideMode get_angular_damp_mode() const { return angular_damp_mode; }
+ 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);
+
+ Vector3 compute_gravity(const Vector3 &p_position) const;
+
+ void body_shape_entered(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
+ bool body_shape_exited(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
+
+ void area_shape_entered(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
+ bool area_shape_exited(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
+
+ bool shape_exited(const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
+ void body_exited(const JPH::BodyID &p_body_id, bool p_notify = true);
+ void area_exited(const JPH::BodyID &p_body_id);
+
+ void call_queries(JPH::Body &p_jolt_body);
+
+ virtual bool has_custom_center_of_mass() const override { return false; }
+ virtual Vector3 get_center_of_mass_custom() const override { return { 0, 0, 0 }; }
+};
+
+#endif // JOLT_AREA_3D_H
diff --git a/modules/jolt_physics/objects/jolt_body_3d.cpp b/modules/jolt_physics/objects/jolt_body_3d.cpp
new file mode 100644
index 000000000000..af0037538ee6
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_body_3d.cpp
@@ -0,0 +1,1439 @@
+/**************************************************************************/
+/* jolt_body_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_body_3d.h"
+
+#include "../joints/jolt_joint_3d.h"
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+#include "../shapes/jolt_shape_3d.h"
+#include "../spaces/jolt_broad_phase_layer.h"
+#include "../spaces/jolt_space_3d.h"
+#include "jolt_area_3d.h"
+#include "jolt_group_filter.h"
+#include "jolt_physics_direct_body_state_3d.h"
+#include "jolt_soft_body_3d.h"
+
+#include "core/error/error_macros.h"
+
+namespace {
+
+template
+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: {
+ return _is_big() ? JoltBroadPhaseLayer::BODY_STATIC_BIG : JoltBroadPhaseLayer::BODY_STATIC;
+ }
+ case PhysicsServer3D::BODY_MODE_KINEMATIC:
+ case PhysicsServer3D::BODY_MODE_RIGID:
+ case PhysicsServer3D::BODY_MODE_RIGID_LINEAR: {
+ return JoltBroadPhaseLayer::BODY_DYNAMIC;
+ }
+ default: {
+ ERR_FAIL_V_MSG(JoltBroadPhaseLayer::BODY_STATIC, vformat("Unhandled body mode: '%d'. This should not happen. Please report this.", mode));
+ }
+ }
+}
+
+JPH::ObjectLayer JoltBody3D::_get_object_layer() const {
+ ERR_FAIL_NULL_V(space, 0);
+
+ return space->map_to_object_layer(_get_broad_phase_layer(), collision_layer, collision_mask);
+}
+
+JPH::EMotionType JoltBody3D::_get_motion_type() const {
+ switch (mode) {
+ case PhysicsServer3D::BODY_MODE_STATIC: {
+ return JPH::EMotionType::Static;
+ }
+ case PhysicsServer3D::BODY_MODE_KINEMATIC: {
+ return JPH::EMotionType::Kinematic;
+ }
+ case PhysicsServer3D::BODY_MODE_RIGID:
+ case PhysicsServer3D::BODY_MODE_RIGID_LINEAR: {
+ return JPH::EMotionType::Dynamic;
+ }
+ default: {
+ ERR_FAIL_V_MSG(JPH::EMotionType::Static, vformat("Unhandled body mode: '%d'. This should not happen. Please report this.", mode));
+ }
+ }
+}
+
+void JoltBody3D::_add_to_space() {
+ jolt_shape = build_shape();
+
+ JPH::CollisionGroup::GroupID group_id = 0;
+ JPH::CollisionGroup::SubGroupID sub_group_id = 0;
+ JoltGroupFilter::encode_object(this, group_id, sub_group_id);
+
+ jolt_settings->mUserData = reinterpret_cast(this);
+ jolt_settings->mObjectLayer = _get_object_layer();
+ jolt_settings->mCollisionGroup = JPH::CollisionGroup(nullptr, group_id, sub_group_id);
+ jolt_settings->mMotionType = _get_motion_type();
+ jolt_settings->mAllowedDOFs = _calculate_allowed_dofs();
+ jolt_settings->mAllowDynamicOrKinematic = true;
+ jolt_settings->mCollideKinematicVsNonDynamic = reports_all_kinematic_contacts();
+ jolt_settings->mUseManifoldReduction = !reports_contacts();
+ jolt_settings->mLinearDamping = 0.0f;
+ jolt_settings->mAngularDamping = 0.0f;
+ jolt_settings->mMaxLinearVelocity = JoltProjectSettings::get_max_linear_velocity();
+ jolt_settings->mMaxAngularVelocity = JoltProjectSettings::get_max_angular_velocity();
+
+ if (JoltProjectSettings::use_enhanced_internal_edge_removal_for_bodies()) {
+ jolt_settings->mEnhancedInternalEdgeRemoval = true;
+ }
+
+ jolt_settings->mOverrideMassProperties = JPH::EOverrideMassProperties::MassAndInertiaProvided;
+ jolt_settings->mMassPropertiesOverride = _calculate_mass_properties();
+
+ jolt_settings->SetShape(jolt_shape);
+
+ const JPH::BodyID new_jolt_id = space->add_rigid_body(*this, *jolt_settings, sleep_initially);
+ if (!new_jolt_id.IsInvalid()) {
+ jolt_id = new_jolt_id;
+ }
+
+ delete jolt_settings;
+ jolt_settings = nullptr;
+}
+
+void JoltBody3D::_integrate_forces(float p_step, JPH::Body &p_jolt_body) {
+ if (!p_jolt_body.IsActive()) {
+ return;
+ }
+
+ _update_gravity(p_jolt_body);
+
+ if (!custom_integrator) {
+ JPH::MotionProperties &motion_properties = *p_jolt_body.GetMotionPropertiesUnchecked();
+
+ JPH::Vec3 linear_velocity = motion_properties.GetLinearVelocity();
+ JPH::Vec3 angular_velocity = motion_properties.GetAngularVelocity();
+
+ // Jolt applies damping differently from Godot Physics, where Godot Physics applies damping before integrating
+ // forces whereas Jolt does it after integrating forces. The way Godot Physics does it seems to yield more
+ // consistent results across different update frequencies when using high (>1) damping values, so we apply the
+ // damping ourselves instead, before any force integration happens.
+
+ linear_velocity *= MAX(1.0f - total_linear_damp * p_step, 0.0f);
+ angular_velocity *= MAX(1.0f - total_angular_damp * p_step, 0.0f);
+
+ linear_velocity += to_jolt(gravity) * p_step;
+
+ motion_properties.SetLinearVelocityClamped(linear_velocity);
+ motion_properties.SetAngularVelocityClamped(angular_velocity);
+
+ p_jolt_body.AddForce(to_jolt(constant_force));
+ p_jolt_body.AddTorque(to_jolt(constant_torque));
+ }
+
+ sync_state = true;
+}
+
+void JoltBody3D::_move_kinematic(float p_step, JPH::Body &p_jolt_body) {
+ p_jolt_body.SetLinearVelocity(JPH::Vec3::sZero());
+ p_jolt_body.SetAngularVelocity(JPH::Vec3::sZero());
+
+ const JPH::RVec3 current_position = p_jolt_body.GetPosition();
+ const JPH::Quat current_rotation = p_jolt_body.GetRotation();
+
+ const JPH::RVec3 new_position = to_jolt_r(kinematic_transform.origin);
+ const JPH::Quat new_rotation = to_jolt(kinematic_transform.basis);
+
+ if (new_position == current_position && new_rotation == current_rotation) {
+ return;
+ }
+
+ p_jolt_body.MoveKinematic(new_position, new_rotation, p_step);
+
+ sync_state = true;
+}
+
+void JoltBody3D::_pre_step_static(float p_step, JPH::Body &p_jolt_body) {
+ // Nothing to do.
+}
+
+void JoltBody3D::_pre_step_rigid(float p_step, JPH::Body &p_jolt_body) {
+ _integrate_forces(p_step, p_jolt_body);
+}
+
+void JoltBody3D::_pre_step_kinematic(float p_step, JPH::Body &p_jolt_body) {
+ _update_gravity(p_jolt_body);
+
+ _move_kinematic(p_step, p_jolt_body);
+
+ if (reports_contacts()) {
+ // This seems to emulate the behavior of Godot Physics, where kinematic bodies are set as active (and thereby
+ // have their state synchronized on every step) only if its max reported contacts is non-zero.
+ sync_state = true;
+ }
+}
+
+JPH::EAllowedDOFs JoltBody3D::_calculate_allowed_dofs() const {
+ if (is_static()) {
+ return JPH::EAllowedDOFs::All;
+ }
+
+ JPH::EAllowedDOFs allowed_dofs = JPH::EAllowedDOFs::All;
+
+ if (is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_X)) {
+ allowed_dofs &= ~JPH::EAllowedDOFs::TranslationX;
+ }
+
+ if (is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Y)) {
+ allowed_dofs &= ~JPH::EAllowedDOFs::TranslationY;
+ }
+
+ if (is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Z)) {
+ allowed_dofs &= ~JPH::EAllowedDOFs::TranslationZ;
+ }
+
+ if (is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_X) || is_rigid_linear()) {
+ allowed_dofs &= ~JPH::EAllowedDOFs::RotationX;
+ }
+
+ if (is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_Y) || is_rigid_linear()) {
+ allowed_dofs &= ~JPH::EAllowedDOFs::RotationY;
+ }
+
+ if (is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_Z) || is_rigid_linear()) {
+ allowed_dofs &= ~JPH::EAllowedDOFs::RotationZ;
+ }
+
+ ERR_FAIL_COND_V_MSG(allowed_dofs == JPH::EAllowedDOFs::None, JPH::EAllowedDOFs::All, vformat("Invalid axis locks for '%s'. Locking all axes is not supported when using Jolt Physics. All axes will be unlocked. Considering freezing the body instead.", to_string()));
+
+ return allowed_dofs;
+}
+
+JPH::MassProperties JoltBody3D::_calculate_mass_properties(const JPH::Shape &p_shape) const {
+ const bool calculate_mass = mass <= 0;
+ const bool calculate_inertia = inertia.x <= 0 || inertia.y <= 0 || inertia.z <= 0;
+
+ JPH::MassProperties mass_properties = p_shape.GetMassProperties();
+
+ if (calculate_mass && calculate_inertia) {
+ // Use the mass properties calculated by the shape
+ } else if (calculate_inertia) {
+ mass_properties.ScaleToMass(mass);
+ } else {
+ mass_properties.mMass = mass;
+ }
+
+ if (inertia.x > 0) {
+ mass_properties.mInertia(0, 0) = (float)inertia.x;
+ mass_properties.mInertia(0, 1) = 0;
+ mass_properties.mInertia(0, 2) = 0;
+ mass_properties.mInertia(1, 0) = 0;
+ mass_properties.mInertia(2, 0) = 0;
+ }
+
+ if (inertia.y > 0) {
+ mass_properties.mInertia(1, 1) = (float)inertia.y;
+ mass_properties.mInertia(1, 0) = 0;
+ mass_properties.mInertia(1, 2) = 0;
+ mass_properties.mInertia(0, 1) = 0;
+ mass_properties.mInertia(2, 1) = 0;
+ }
+
+ if (inertia.z > 0) {
+ mass_properties.mInertia(2, 2) = (float)inertia.z;
+ mass_properties.mInertia(2, 0) = 0;
+ mass_properties.mInertia(2, 1) = 0;
+ mass_properties.mInertia(0, 2) = 0;
+ mass_properties.mInertia(1, 2) = 0;
+ }
+
+ mass_properties.mInertia(3, 3) = 1.0f;
+
+ return mass_properties;
+}
+
+JPH::MassProperties JoltBody3D::_calculate_mass_properties() const {
+ return _calculate_mass_properties(*jolt_shape);
+}
+
+void JoltBody3D::_update_mass_properties() {
+ if (!in_space()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->GetMotionPropertiesUnchecked()->SetMassProperties(_calculate_allowed_dofs(), _calculate_mass_properties());
+}
+
+void JoltBody3D::_update_gravity(JPH::Body &p_jolt_body) {
+ gravity = Vector3();
+
+ const Vector3 position = to_godot(p_jolt_body.GetPosition());
+
+ bool gravity_done = false;
+
+ for (const JoltArea3D *area : areas) {
+ gravity_done = integrate(gravity, area->get_gravity_mode(), [&]() { return area->compute_gravity(position); });
+
+ if (gravity_done) {
+ break;
+ }
+ }
+
+ if (!gravity_done) {
+ gravity += space->get_default_area()->compute_gravity(position);
+ }
+
+ gravity *= gravity_scale;
+}
+
+void JoltBody3D::_update_damp() {
+ if (!in_space()) {
+ return;
+ }
+
+ total_linear_damp = 0.0;
+ total_angular_damp = 0.0;
+
+ bool linear_damp_done = linear_damp_mode == PhysicsServer3D::BODY_DAMP_MODE_REPLACE;
+ bool angular_damp_done = angular_damp_mode == PhysicsServer3D::BODY_DAMP_MODE_REPLACE;
+
+ for (const JoltArea3D *area : areas) {
+ if (!linear_damp_done) {
+ linear_damp_done = integrate(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(), [&]() { return area->get_angular_damp(); });
+ }
+
+ if (linear_damp_done && angular_damp_done) {
+ break;
+ }
+ }
+
+ const JoltArea3D *default_area = space->get_default_area();
+
+ if (!linear_damp_done) {
+ total_linear_damp += default_area->get_linear_damp();
+ }
+
+ if (!angular_damp_done) {
+ total_angular_damp += default_area->get_angular_damp();
+ }
+
+ switch (linear_damp_mode) {
+ case PhysicsServer3D::BODY_DAMP_MODE_COMBINE: {
+ total_linear_damp += linear_damp;
+ } break;
+ case PhysicsServer3D::BODY_DAMP_MODE_REPLACE: {
+ total_linear_damp = linear_damp;
+ } break;
+ }
+
+ switch (angular_damp_mode) {
+ case PhysicsServer3D::BODY_DAMP_MODE_COMBINE: {
+ total_angular_damp += angular_damp;
+ } break;
+ case PhysicsServer3D::BODY_DAMP_MODE_REPLACE: {
+ total_angular_damp = angular_damp;
+ } break;
+ }
+
+ _motion_changed();
+}
+
+void JoltBody3D::_update_kinematic_transform() {
+ if (is_kinematic()) {
+ kinematic_transform = get_transform_unscaled();
+ }
+}
+
+void JoltBody3D::_update_joint_constraints() {
+ for (JoltJoint3D *joint : joints) {
+ joint->rebuild();
+ }
+}
+
+void JoltBody3D::_update_possible_kinematic_contacts() {
+ const bool value = reports_all_kinematic_contacts();
+
+ if (!in_space()) {
+ jolt_settings->mCollideKinematicVsNonDynamic = value;
+ } else {
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->SetCollideKinematicVsNonDynamic(value);
+ }
+}
+
+void JoltBody3D::_destroy_joint_constraints() {
+ for (JoltJoint3D *joint : joints) {
+ joint->destroy();
+ }
+}
+
+void JoltBody3D::_exit_all_areas() {
+ for (JoltArea3D *area : areas) {
+ area->body_exited(jolt_id, false);
+ }
+
+ areas.clear();
+}
+
+void JoltBody3D::_update_group_filter() {
+ JPH::GroupFilter *group_filter = !exceptions.is_empty() ? JoltGroupFilter::instance : nullptr;
+
+ if (!in_space()) {
+ jolt_settings->mCollisionGroup.SetGroupFilter(group_filter);
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->GetCollisionGroup().SetGroupFilter(group_filter);
+}
+
+void JoltBody3D::_mode_changed() {
+ _update_object_layer();
+ _update_kinematic_transform();
+ _update_mass_properties();
+ wake_up();
+}
+
+void JoltBody3D::_shapes_built() {
+ JoltShapedObject3D::_shapes_built();
+
+ _update_mass_properties();
+ _update_joint_constraints();
+ wake_up();
+}
+
+void JoltBody3D::_space_changing() {
+ JoltShapedObject3D::_space_changing();
+
+ sleep_initially = is_sleeping();
+
+ _destroy_joint_constraints();
+ _exit_all_areas();
+}
+
+void JoltBody3D::_space_changed() {
+ JoltShapedObject3D::_space_changed();
+
+ _update_kinematic_transform();
+ _update_group_filter();
+ _update_joint_constraints();
+ _areas_changed();
+
+ sync_state = false;
+}
+
+void JoltBody3D::_areas_changed() {
+ _update_damp();
+ wake_up();
+}
+
+void JoltBody3D::_joints_changed() {
+ wake_up();
+}
+
+void JoltBody3D::_transform_changed() {
+ wake_up();
+}
+
+void JoltBody3D::_motion_changed() {
+ wake_up();
+}
+
+void JoltBody3D::_exceptions_changed() {
+ _update_group_filter();
+}
+
+void JoltBody3D::_axis_lock_changed() {
+ _update_mass_properties();
+ wake_up();
+}
+
+void JoltBody3D::_contact_reporting_changed() {
+ _update_possible_kinematic_contacts();
+ wake_up();
+}
+
+JoltBody3D::JoltBody3D() :
+ JoltShapedObject3D(OBJECT_TYPE_BODY) {
+}
+
+JoltBody3D::~JoltBody3D() {
+ if (direct_state != nullptr) {
+ memdelete(direct_state);
+ direct_state = nullptr;
+ }
+}
+
+void JoltBody3D::set_transform(Transform3D p_transform) {
+ JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, vformat("An invalid transform was passed to physics body '%s'.", to_string()));
+
+ const Vector3 new_scale = p_transform.basis.get_scale();
+
+ // Ideally we would do an exact comparison here, but due to floating-point precision this would be invalidated very often.
+ if (!scale.is_equal_approx(new_scale)) {
+ scale = new_scale;
+ _shapes_changed();
+ }
+
+ p_transform.basis.orthonormalize();
+
+ if (!in_space()) {
+ jolt_settings->mPosition = to_jolt_r(p_transform.origin);
+ jolt_settings->mRotation = to_jolt(p_transform.basis);
+ } else if (is_kinematic()) {
+ kinematic_transform = p_transform;
+ } else {
+ space->get_body_iface().SetPositionAndRotation(jolt_id, to_jolt_r(p_transform.origin), to_jolt(p_transform.basis), JPH::EActivation::DontActivate);
+ }
+
+ _transform_changed();
+}
+
+Variant JoltBody3D::get_state(PhysicsServer3D::BodyState p_state) const {
+ switch (p_state) {
+ case PhysicsServer3D::BODY_STATE_TRANSFORM: {
+ return get_transform_scaled();
+ }
+ case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {
+ return get_linear_velocity();
+ }
+ case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {
+ return get_angular_velocity();
+ }
+ case PhysicsServer3D::BODY_STATE_SLEEPING: {
+ return is_sleeping();
+ }
+ case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
+ return can_sleep();
+ }
+ default: {
+ ERR_FAIL_V_MSG(Variant(), vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
+ }
+ }
+}
+
+void JoltBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_value) {
+ switch (p_state) {
+ case PhysicsServer3D::BODY_STATE_TRANSFORM: {
+ set_transform(p_value);
+ } break;
+ case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {
+ set_linear_velocity(p_value);
+ } break;
+ case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {
+ set_angular_velocity(p_value);
+ } break;
+ case PhysicsServer3D::BODY_STATE_SLEEPING: {
+ set_is_sleeping(p_value);
+ } break;
+ case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
+ set_can_sleep(p_value);
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
+ } break;
+ }
+}
+
+Variant JoltBody3D::get_param(PhysicsServer3D::BodyParameter p_param) const {
+ switch (p_param) {
+ case PhysicsServer3D::BODY_PARAM_BOUNCE: {
+ return get_bounce();
+ }
+ case PhysicsServer3D::BODY_PARAM_FRICTION: {
+ return get_friction();
+ }
+ case PhysicsServer3D::BODY_PARAM_MASS: {
+ return get_mass();
+ }
+ case PhysicsServer3D::BODY_PARAM_INERTIA: {
+ return get_inertia();
+ }
+ case PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS: {
+ return get_center_of_mass_custom();
+ }
+ case PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE: {
+ return get_gravity_scale();
+ }
+ case PhysicsServer3D::BODY_PARAM_LINEAR_DAMP_MODE: {
+ return get_linear_damp_mode();
+ }
+ case PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP_MODE: {
+ return get_angular_damp_mode();
+ }
+ case PhysicsServer3D::BODY_PARAM_LINEAR_DAMP: {
+ return get_linear_damp();
+ }
+ case PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP: {
+ return get_angular_damp();
+ }
+ default: {
+ ERR_FAIL_V_MSG(Variant(), vformat("Unhandled body parameter: '%d'. This should not happen. Please report this.", p_param));
+ }
+ }
+}
+
+void JoltBody3D::set_param(PhysicsServer3D::BodyParameter p_param, const Variant &p_value) {
+ switch (p_param) {
+ case PhysicsServer3D::BODY_PARAM_BOUNCE: {
+ set_bounce(p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_FRICTION: {
+ set_friction(p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_MASS: {
+ set_mass(p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_INERTIA: {
+ set_inertia(p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS: {
+ set_center_of_mass_custom(p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE: {
+ set_gravity_scale(p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_LINEAR_DAMP_MODE: {
+ set_linear_damp_mode((DampMode)(int)p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP_MODE: {
+ set_angular_damp_mode((DampMode)(int)p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_LINEAR_DAMP: {
+ set_linear_damp(p_value);
+ } break;
+ case PhysicsServer3D::BODY_PARAM_ANGULAR_DAMP: {
+ set_angular_damp(p_value);
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled body parameter: '%d'. This should not happen. Please report this.", p_param));
+ } break;
+ }
+}
+
+void JoltBody3D::set_custom_integrator(bool p_enabled) {
+ if (custom_integrator == p_enabled) {
+ return;
+ }
+
+ custom_integrator = p_enabled;
+
+ if (!in_space()) {
+ _motion_changed();
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->ResetForce();
+ body->ResetTorque();
+
+ _motion_changed();
+}
+
+bool JoltBody3D::is_sleeping() const {
+ if (!in_space()) {
+ return sleep_initially;
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), false);
+
+ return !body->IsActive();
+}
+
+void JoltBody3D::set_is_sleeping(bool p_enabled) {
+ if (!in_space()) {
+ sleep_initially = p_enabled;
+ return;
+ }
+
+ JPH::BodyInterface &body_iface = space->get_body_iface();
+
+ if (p_enabled) {
+ body_iface.DeactivateBody(jolt_id);
+ } else {
+ body_iface.ActivateBody(jolt_id);
+ }
+}
+
+bool JoltBody3D::can_sleep() const {
+ if (!in_space()) {
+ return jolt_settings->mAllowSleeping;
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), false);
+
+ return body->GetAllowSleeping();
+}
+
+void JoltBody3D::set_can_sleep(bool p_enabled) {
+ if (!in_space()) {
+ jolt_settings->mAllowSleeping = p_enabled;
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->SetAllowSleeping(p_enabled);
+}
+
+Basis JoltBody3D::get_principal_inertia_axes() const {
+ ERR_FAIL_NULL_V_MSG(space, Basis(), vformat("Failed to retrieve principal inertia axes of '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(is_static() || is_kinematic())) {
+ return Basis();
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Basis());
+
+ return to_godot(body->GetRotation() * body->GetMotionProperties()->GetInertiaRotation());
+}
+
+Vector3 JoltBody3D::get_inverse_inertia() const {
+ ERR_FAIL_NULL_V_MSG(space, Vector3(), vformat("Failed to retrieve inverse inertia of '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(is_static() || is_kinematic())) {
+ return Vector3();
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Vector3());
+
+ const JPH::MotionProperties &motion_properties = *body->GetMotionPropertiesUnchecked();
+
+ return to_godot(motion_properties.GetLocalSpaceInverseInertia().GetDiagonal3());
+}
+
+Basis JoltBody3D::get_inverse_inertia_tensor() const {
+ ERR_FAIL_NULL_V_MSG(space, Basis(), vformat("Failed to retrieve inverse inertia tensor of '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(is_static() || is_kinematic())) {
+ return Basis();
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Basis());
+
+ return to_godot(body->GetInverseInertia()).basis;
+}
+
+void JoltBody3D::set_linear_velocity(const Vector3 &p_velocity) {
+ if (is_static() || is_kinematic()) {
+ linear_surface_velocity = p_velocity;
+ _motion_changed();
+ return;
+ }
+
+ if (!in_space()) {
+ jolt_settings->mLinearVelocity = to_jolt(p_velocity);
+ _motion_changed();
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->GetMotionPropertiesUnchecked()->SetLinearVelocityClamped(to_jolt(p_velocity));
+
+ _motion_changed();
+}
+
+void JoltBody3D::set_angular_velocity(const Vector3 &p_velocity) {
+ if (is_static() || is_kinematic()) {
+ angular_surface_velocity = p_velocity;
+ _motion_changed();
+ return;
+ }
+
+ if (!in_space()) {
+ jolt_settings->mAngularVelocity = to_jolt(p_velocity);
+ _motion_changed();
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->GetMotionPropertiesUnchecked()->SetAngularVelocityClamped(to_jolt(p_velocity));
+
+ _motion_changed();
+}
+
+void JoltBody3D::set_axis_velocity(const Vector3 &p_axis_velocity) {
+ const Vector3 axis = p_axis_velocity.normalized();
+
+ if (!in_space()) {
+ Vector3 linear_velocity = to_godot(jolt_settings->mLinearVelocity);
+ linear_velocity -= axis * axis.dot(linear_velocity);
+ linear_velocity += p_axis_velocity;
+ jolt_settings->mLinearVelocity = to_jolt(linear_velocity);
+ } else {
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ Vector3 linear_velocity = get_linear_velocity();
+ linear_velocity -= axis * axis.dot(linear_velocity);
+ linear_velocity += p_axis_velocity;
+ set_linear_velocity(linear_velocity);
+ }
+
+ _motion_changed();
+}
+
+Vector3 JoltBody3D::get_velocity_at_position(const Vector3 &p_position) const {
+ if (unlikely(!in_space())) {
+ return Vector3();
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Vector3());
+
+ const JPH::MotionProperties &motion_properties = *body->GetMotionPropertiesUnchecked();
+
+ const Vector3 total_linear_velocity = to_godot(motion_properties.GetLinearVelocity()) + linear_surface_velocity;
+ const Vector3 total_angular_velocity = to_godot(motion_properties.GetAngularVelocity()) + angular_surface_velocity;
+ const Vector3 com_to_pos = p_position - to_godot(body->GetCenterOfMassPosition());
+
+ return total_linear_velocity + total_angular_velocity.cross(com_to_pos);
+}
+
+void JoltBody3D::set_center_of_mass_custom(const Vector3 &p_center_of_mass) {
+ if (custom_center_of_mass && p_center_of_mass == center_of_mass_custom) {
+ return;
+ }
+
+ custom_center_of_mass = true;
+ center_of_mass_custom = p_center_of_mass;
+
+ _shapes_changed();
+}
+
+void JoltBody3D::set_max_contacts_reported(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+
+ if (unlikely((int)contacts.size() == p_count)) {
+ return;
+ }
+
+ contacts.resize(p_count);
+ contact_count = MIN(contact_count, p_count);
+
+ const bool use_manifold_reduction = !reports_contacts();
+
+ if (!in_space()) {
+ jolt_settings->mUseManifoldReduction = use_manifold_reduction;
+ _contact_reporting_changed();
+ return;
+ }
+
+ JPH::BodyInterface &body_iface = space->get_body_iface();
+
+ body_iface.SetUseManifoldReduction(jolt_id, use_manifold_reduction);
+
+ _contact_reporting_changed();
+}
+
+bool JoltBody3D::reports_all_kinematic_contacts() const {
+ return reports_contacts() && JoltProjectSettings::should_generate_all_kinematic_contacts();
+}
+
+void JoltBody3D::add_contact(const JoltBody3D *p_collider, float p_depth, int p_shape_index, int p_collider_shape_index, const Vector3 &p_normal, const Vector3 &p_position, const Vector3 &p_collider_position, const Vector3 &p_velocity, const Vector3 &p_collider_velocity, const Vector3 &p_impulse) {
+ const int max_contacts = get_max_contacts_reported();
+
+ if (max_contacts == 0) {
+ return;
+ }
+
+ Contact *contact = nullptr;
+
+ if (contact_count < max_contacts) {
+ contact = &contacts[contact_count++];
+ } else {
+ Contact *shallowest_contact = &contacts[0];
+
+ for (int i = 1; i < (int)contacts.size(); i++) {
+ Contact &other_contact = contacts[i];
+ if (other_contact.depth < shallowest_contact->depth) {
+ shallowest_contact = &other_contact;
+ }
+ }
+
+ if (shallowest_contact->depth < p_depth) {
+ contact = shallowest_contact;
+ }
+ }
+
+ if (contact != nullptr) {
+ contact->normal = p_normal;
+ contact->position = p_position;
+ contact->collider_position = p_collider_position;
+ contact->velocity = p_velocity;
+ contact->collider_velocity = p_collider_velocity;
+ contact->impulse = p_impulse;
+ contact->collider_id = p_collider->get_instance_id();
+ contact->collider_rid = p_collider->get_rid();
+ contact->shape_index = p_shape_index;
+ contact->collider_shape_index = p_collider_shape_index;
+ }
+}
+
+void JoltBody3D::reset_mass_properties() {
+ if (custom_center_of_mass) {
+ custom_center_of_mass = false;
+ center_of_mass_custom.zero();
+
+ _shapes_changed();
+ }
+
+ inertia.zero();
+
+ _update_mass_properties();
+}
+
+void JoltBody3D::apply_force(const Vector3 &p_force, const Vector3 &p_position) {
+ ERR_FAIL_NULL_MSG(space, vformat("Failed to apply force to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(!is_rigid())) {
+ return;
+ }
+
+ if (custom_integrator || p_force == Vector3()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->AddForce(to_jolt(p_force), body->GetPosition() + to_jolt(p_position));
+
+ _motion_changed();
+}
+
+void JoltBody3D::apply_central_force(const Vector3 &p_force) {
+ ERR_FAIL_NULL_MSG(space, vformat("Failed to apply central force to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(!is_rigid())) {
+ return;
+ }
+
+ if (custom_integrator || p_force == Vector3()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->AddForce(to_jolt(p_force));
+
+ _motion_changed();
+}
+
+void JoltBody3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) {
+ ERR_FAIL_NULL_MSG(space, vformat("Failed to apply impulse to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(!is_rigid())) {
+ return;
+ }
+
+ if (p_impulse == Vector3()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->AddImpulse(to_jolt(p_impulse), body->GetPosition() + to_jolt(p_position));
+
+ _motion_changed();
+}
+
+void JoltBody3D::apply_central_impulse(const Vector3 &p_impulse) {
+ ERR_FAIL_NULL_MSG(space, vformat("Failed to apply central impulse to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(!is_rigid())) {
+ return;
+ }
+
+ if (p_impulse == Vector3()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->AddImpulse(to_jolt(p_impulse));
+
+ _motion_changed();
+}
+
+void JoltBody3D::apply_torque(const Vector3 &p_torque) {
+ ERR_FAIL_NULL_MSG(space, vformat("Failed to apply torque to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(!is_rigid())) {
+ return;
+ }
+
+ if (custom_integrator || p_torque == Vector3()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->AddTorque(to_jolt(p_torque));
+
+ _motion_changed();
+}
+
+void JoltBody3D::apply_torque_impulse(const Vector3 &p_impulse) {
+ ERR_FAIL_NULL_MSG(space, vformat("Failed to apply torque impulse to '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ if (unlikely(!is_rigid())) {
+ return;
+ }
+
+ if (p_impulse == Vector3()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->AddAngularImpulse(to_jolt(p_impulse));
+
+ _motion_changed();
+}
+
+void JoltBody3D::add_constant_central_force(const Vector3 &p_force) {
+ if (p_force == Vector3()) {
+ return;
+ }
+
+ constant_force += p_force;
+
+ _motion_changed();
+}
+
+void JoltBody3D::add_constant_force(const Vector3 &p_force, const Vector3 &p_position) {
+ if (p_force == Vector3()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ constant_force += p_force;
+ constant_torque += (p_position - get_center_of_mass_relative()).cross(p_force);
+
+ _motion_changed();
+}
+
+void JoltBody3D::add_constant_torque(const Vector3 &p_torque) {
+ if (p_torque == Vector3()) {
+ return;
+ }
+
+ constant_torque += p_torque;
+
+ _motion_changed();
+}
+
+Vector3 JoltBody3D::get_constant_force() const {
+ return constant_force;
+}
+
+void JoltBody3D::set_constant_force(const Vector3 &p_force) {
+ if (constant_force == p_force) {
+ return;
+ }
+
+ constant_force = p_force;
+
+ _motion_changed();
+}
+
+Vector3 JoltBody3D::get_constant_torque() const {
+ return constant_torque;
+}
+
+void JoltBody3D::set_constant_torque(const Vector3 &p_torque) {
+ if (constant_torque == p_torque) {
+ return;
+ }
+
+ constant_torque = p_torque;
+
+ _motion_changed();
+}
+
+void JoltBody3D::add_collision_exception(const RID &p_excepted_body) {
+ exceptions.push_back(p_excepted_body);
+
+ _exceptions_changed();
+}
+
+void JoltBody3D::remove_collision_exception(const RID &p_excepted_body) {
+ exceptions.erase(p_excepted_body);
+
+ _exceptions_changed();
+}
+
+bool JoltBody3D::has_collision_exception(const RID &p_excepted_body) const {
+ return exceptions.find(p_excepted_body) >= 0;
+}
+
+void JoltBody3D::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 JoltBody3D::remove_area(JoltArea3D *p_area) {
+ areas.erase(p_area);
+
+ _areas_changed();
+}
+
+void JoltBody3D::add_joint(JoltJoint3D *p_joint) {
+ joints.push_back(p_joint);
+
+ _joints_changed();
+}
+
+void JoltBody3D::remove_joint(JoltJoint3D *p_joint) {
+ joints.erase(p_joint);
+
+ _joints_changed();
+}
+
+void JoltBody3D::call_queries(JPH::Body &p_jolt_body) {
+ if (!sync_state) {
+ return;
+ }
+
+ if (custom_integration_callback.is_valid()) {
+ if (custom_integration_userdata.get_type() != Variant::NIL) {
+ static thread_local Array arguments = []() {
+ Array array;
+ array.resize(2);
+ return array;
+ }();
+
+ arguments[0] = get_direct_state();
+ arguments[1] = custom_integration_userdata;
+
+ custom_integration_callback.callv(arguments);
+ } else {
+ static thread_local Array arguments = []() {
+ Array array;
+ array.resize(1);
+ return array;
+ }();
+
+ arguments[0] = get_direct_state();
+
+ custom_integration_callback.callv(arguments);
+ }
+ }
+
+ if (state_sync_callback.is_valid()) {
+ static thread_local Array arguments = []() {
+ Array array;
+ array.resize(1);
+ return array;
+ }();
+
+ arguments[0] = get_direct_state();
+
+ state_sync_callback.callv(arguments);
+ }
+
+ sync_state = false;
+}
+
+void JoltBody3D::pre_step(float p_step, JPH::Body &p_jolt_body) {
+ JoltObject3D::pre_step(p_step, p_jolt_body);
+
+ switch (mode) {
+ case PhysicsServer3D::BODY_MODE_STATIC: {
+ _pre_step_static(p_step, p_jolt_body);
+ } break;
+ case PhysicsServer3D::BODY_MODE_RIGID:
+ case PhysicsServer3D::BODY_MODE_RIGID_LINEAR: {
+ _pre_step_rigid(p_step, p_jolt_body);
+ } break;
+ case PhysicsServer3D::BODY_MODE_KINEMATIC: {
+ _pre_step_kinematic(p_step, p_jolt_body);
+ } break;
+ }
+
+ contact_count = 0;
+}
+
+JoltPhysicsDirectBodyState3D *JoltBody3D::get_direct_state() {
+ if (direct_state == nullptr) {
+ direct_state = memnew(JoltPhysicsDirectBodyState3D(this));
+ }
+
+ return direct_state;
+}
+
+void JoltBody3D::set_mode(PhysicsServer3D::BodyMode p_mode) {
+ if (p_mode == mode) {
+ return;
+ }
+
+ mode = p_mode;
+
+ if (!in_space()) {
+ _mode_changed();
+ return;
+ }
+
+ const JPH::EMotionType motion_type = _get_motion_type();
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ if (motion_type == JPH::EMotionType::Static) {
+ put_to_sleep();
+ }
+
+ body->SetMotionType(motion_type);
+
+ if (motion_type != JPH::EMotionType::Static) {
+ wake_up();
+ }
+
+ if (motion_type == JPH::EMotionType::Kinematic) {
+ body->SetLinearVelocity(JPH::Vec3::sZero());
+ body->SetAngularVelocity(JPH::Vec3::sZero());
+ }
+
+ linear_surface_velocity = Vector3();
+ angular_surface_velocity = Vector3();
+
+ _mode_changed();
+}
+
+bool JoltBody3D::is_ccd_enabled() const {
+ if (!in_space()) {
+ return jolt_settings->mMotionQuality == JPH::EMotionQuality::LinearCast;
+ }
+
+ const JPH::BodyInterface &body_iface = space->get_body_iface();
+
+ return body_iface.GetMotionQuality(jolt_id) == JPH::EMotionQuality::LinearCast;
+}
+
+void JoltBody3D::set_ccd_enabled(bool p_enabled) {
+ const JPH::EMotionQuality motion_quality = p_enabled ? JPH::EMotionQuality::LinearCast : JPH::EMotionQuality::Discrete;
+
+ if (!in_space()) {
+ jolt_settings->mMotionQuality = motion_quality;
+ return;
+ }
+
+ JPH::BodyInterface &body_iface = space->get_body_iface();
+
+ body_iface.SetMotionQuality(jolt_id, motion_quality);
+}
+
+void JoltBody3D::set_mass(float p_mass) {
+ if (p_mass != mass) {
+ mass = p_mass;
+ _update_mass_properties();
+ }
+}
+
+void JoltBody3D::set_inertia(const Vector3 &p_inertia) {
+ if (p_inertia != inertia) {
+ inertia = p_inertia;
+ _update_mass_properties();
+ }
+}
+
+float JoltBody3D::get_bounce() const {
+ if (!in_space()) {
+ return jolt_settings->mRestitution;
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), 0.0f);
+
+ return body->GetRestitution();
+}
+
+void JoltBody3D::set_bounce(float p_bounce) {
+ if (!in_space()) {
+ jolt_settings->mRestitution = p_bounce;
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->SetRestitution(p_bounce);
+}
+
+float JoltBody3D::get_friction() const {
+ if (!in_space()) {
+ return jolt_settings->mFriction;
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), 0.0f);
+
+ return body->GetFriction();
+}
+
+void JoltBody3D::set_friction(float p_friction) {
+ if (!in_space()) {
+ jolt_settings->mFriction = p_friction;
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->SetFriction(p_friction);
+}
+
+void JoltBody3D::set_gravity_scale(float p_scale) {
+ if (gravity_scale == p_scale) {
+ return;
+ }
+
+ gravity_scale = p_scale;
+
+ _motion_changed();
+}
+
+void JoltBody3D::set_linear_damp(float p_damp) {
+ p_damp = MAX(0.0f, p_damp);
+
+ if (p_damp == linear_damp) {
+ return;
+ }
+
+ linear_damp = p_damp;
+
+ _update_damp();
+}
+
+void JoltBody3D::set_angular_damp(float p_damp) {
+ p_damp = MAX(0.0f, p_damp);
+
+ if (p_damp == angular_damp) {
+ return;
+ }
+
+ angular_damp = p_damp;
+
+ _update_damp();
+}
+
+bool JoltBody3D::is_axis_locked(PhysicsServer3D::BodyAxis p_axis) const {
+ return (locked_axes & (uint32_t)p_axis) != 0;
+}
+
+void JoltBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_enabled) {
+ const uint32_t previous_locked_axes = locked_axes;
+
+ if (p_enabled) {
+ locked_axes |= (uint32_t)p_axis;
+ } else {
+ locked_axes &= ~(uint32_t)p_axis;
+ }
+
+ if (previous_locked_axes != locked_axes) {
+ _axis_lock_changed();
+ }
+}
+
+bool JoltBody3D::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);
+}
+
+bool JoltBody3D::can_interact_with(const JoltSoftBody3D &p_other) const {
+ return p_other.can_interact_with(*this);
+}
+
+bool JoltBody3D::can_interact_with(const JoltArea3D &p_other) const {
+ return p_other.can_interact_with(*this);
+}
diff --git a/modules/jolt_physics/objects/jolt_body_3d.h b/modules/jolt_physics/objects/jolt_body_3d.h
new file mode 100644
index 000000000000..50a5c2a293fd
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_body_3d.h
@@ -0,0 +1,304 @@
+/**************************************************************************/
+/* jolt_body_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_BODY_3D_H
+#define JOLT_BODY_3D_H
+
+#include "jolt_physics_direct_body_state_3d.h"
+#include "jolt_shaped_object_3d.h"
+
+class JoltArea3D;
+class JoltJoint3D;
+class JoltSoftBody3D;
+
+class JoltBody3D final : public JoltShapedObject3D {
+public:
+ typedef PhysicsServer3D::BodyDampMode DampMode;
+
+ struct Contact {
+ Vector3 normal;
+ Vector3 position;
+ Vector3 collider_position;
+ Vector3 velocity;
+ Vector3 collider_velocity;
+ Vector3 impulse;
+ ObjectID collider_id;
+ RID collider_rid;
+ float depth = 0.0f;
+ int shape_index = 0;
+ int collider_shape_index = 0;
+ };
+
+private:
+ LocalVector exceptions;
+ LocalVector contacts;
+ LocalVector areas;
+ LocalVector joints;
+
+ Variant custom_integration_userdata;
+
+ Transform3D kinematic_transform;
+
+ Vector3 inertia;
+ Vector3 center_of_mass_custom;
+ Vector3 constant_force;
+ Vector3 constant_torque;
+ Vector3 linear_surface_velocity;
+ Vector3 angular_surface_velocity;
+ Vector3 gravity;
+
+ Callable state_sync_callback;
+ Callable custom_integration_callback;
+
+ JoltPhysicsDirectBodyState3D *direct_state = nullptr;
+
+ PhysicsServer3D::BodyMode mode = PhysicsServer3D::BODY_MODE_RIGID;
+
+ DampMode linear_damp_mode = PhysicsServer3D::BODY_DAMP_MODE_COMBINE;
+ DampMode angular_damp_mode = PhysicsServer3D::BODY_DAMP_MODE_COMBINE;
+
+ float mass = 1.0f;
+ float linear_damp = 0.0f;
+ 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;
+
+ uint32_t locked_axes = 0;
+
+ bool sync_state = false;
+ bool sleep_initially = false;
+ bool custom_center_of_mass = false;
+ bool custom_integrator = false;
+
+ virtual JPH::BroadPhaseLayer _get_broad_phase_layer() const override;
+ virtual JPH::ObjectLayer _get_object_layer() const override;
+
+ virtual JPH::EMotionType _get_motion_type() const override;
+
+ virtual void _add_to_space() override;
+
+ void _integrate_forces(float p_step, JPH::Body &p_jolt_body);
+
+ void _move_kinematic(float p_step, JPH::Body &p_jolt_body);
+
+ void _pre_step_static(float p_step, JPH::Body &p_jolt_body);
+ void _pre_step_rigid(float p_step, JPH::Body &p_jolt_body);
+ void _pre_step_kinematic(float p_step, JPH::Body &p_jolt_body);
+
+ JPH::EAllowedDOFs _calculate_allowed_dofs() const;
+
+ JPH::MassProperties _calculate_mass_properties(const JPH::Shape &p_shape) const;
+ JPH::MassProperties _calculate_mass_properties() const;
+
+ void _update_mass_properties();
+ void _update_gravity(JPH::Body &p_jolt_body);
+ void _update_damp();
+ void _update_kinematic_transform();
+ void _update_group_filter();
+ void _update_joint_constraints();
+ void _update_possible_kinematic_contacts();
+
+ void _destroy_joint_constraints();
+
+ void _exit_all_areas();
+
+ void _mode_changed();
+ virtual void _shapes_built() override;
+ virtual void _space_changing() override;
+ virtual void _space_changed() override;
+ void _areas_changed();
+ void _joints_changed();
+ void _transform_changed();
+ void _motion_changed();
+ void _exceptions_changed();
+ void _axis_lock_changed();
+ void _contact_reporting_changed();
+
+public:
+ JoltBody3D();
+ virtual ~JoltBody3D() override;
+
+ void set_transform(Transform3D p_transform);
+
+ Variant get_state(PhysicsServer3D::BodyState p_state) const;
+ void set_state(PhysicsServer3D::BodyState p_state, const Variant &p_value);
+
+ Variant get_param(PhysicsServer3D::BodyParameter p_param) const;
+ void set_param(PhysicsServer3D::BodyParameter p_param, const Variant &p_value);
+
+ bool has_state_sync_callback() const { return state_sync_callback.is_valid(); }
+ void set_state_sync_callback(const Callable &p_callback) { state_sync_callback = p_callback; }
+
+ bool has_custom_integration_callback() const { return custom_integration_callback.is_valid(); }
+ void set_custom_integration_callback(const Callable &p_callback, const Variant &p_userdata) {
+ custom_integration_callback = p_callback;
+ custom_integration_userdata = p_userdata;
+ }
+
+ bool has_custom_integrator() const { return custom_integrator; }
+ void set_custom_integrator(bool p_enabled);
+
+ bool is_sleeping() const;
+ void set_is_sleeping(bool p_enabled);
+
+ void put_to_sleep() { set_is_sleeping(true); }
+ void wake_up() { set_is_sleeping(false); }
+
+ bool can_sleep() const;
+ void set_can_sleep(bool p_enabled);
+
+ Basis get_principal_inertia_axes() const;
+ Vector3 get_inverse_inertia() const;
+ Basis get_inverse_inertia_tensor() const;
+
+ void set_linear_velocity(const Vector3 &p_velocity);
+ void set_angular_velocity(const Vector3 &p_velocity);
+ void set_axis_velocity(const Vector3 &p_axis_velocity);
+
+ virtual Vector3 get_velocity_at_position(const Vector3 &p_position) const override;
+
+ virtual bool has_custom_center_of_mass() const override { return custom_center_of_mass; }
+ virtual Vector3 get_center_of_mass_custom() const override { return center_of_mass_custom; }
+ void set_center_of_mass_custom(const Vector3 &p_center_of_mass);
+
+ int get_max_contacts_reported() const { return contacts.size(); }
+ void set_max_contacts_reported(int p_count);
+
+ int get_contact_count() const { return contact_count; }
+ const Contact &get_contact(int p_index) { return contacts[p_index]; }
+ virtual bool reports_contacts() const override { return !contacts.is_empty(); }
+
+ bool reports_all_kinematic_contacts() const;
+
+ void add_contact(const JoltBody3D *p_collider, float p_depth, int p_shape_index, int p_collider_shape_index, const Vector3 &p_normal, const Vector3 &p_position, const Vector3 &p_collider_position, const Vector3 &p_velocity, const Vector3 &p_collider_velocity, const Vector3 &p_impulse);
+
+ void reset_mass_properties();
+
+ void apply_force(const Vector3 &p_force, const Vector3 &p_position);
+ void apply_central_force(const Vector3 &p_force);
+ void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position);
+
+ void apply_central_impulse(const Vector3 &p_impulse);
+ void apply_torque(const Vector3 &p_torque);
+ void apply_torque_impulse(const Vector3 &p_impulse);
+
+ void add_constant_central_force(const Vector3 &p_force);
+ void add_constant_force(const Vector3 &p_force, const Vector3 &p_position);
+ void add_constant_torque(const Vector3 &p_torque);
+
+ Vector3 get_constant_force() const;
+ void set_constant_force(const Vector3 &p_force);
+
+ Vector3 get_constant_torque() const;
+ void set_constant_torque(const Vector3 &p_torque);
+
+ Vector3 get_linear_surface_velocity() const { return linear_surface_velocity; }
+ Vector3 get_angular_surface_velocity() const { return angular_surface_velocity; }
+
+ void add_collision_exception(const RID &p_excepted_body);
+ void remove_collision_exception(const RID &p_excepted_body);
+ bool has_collision_exception(const RID &p_excepted_body) const;
+
+ const LocalVector &get_collision_exceptions() const { return exceptions; }
+
+ void add_area(JoltArea3D *p_area);
+ void remove_area(JoltArea3D *p_area);
+
+ void add_joint(JoltJoint3D *p_joint);
+ void remove_joint(JoltJoint3D *p_joint);
+
+ void call_queries(JPH::Body &p_jolt_body);
+
+ virtual void pre_step(float p_step, JPH::Body &p_jolt_body) override;
+
+ JoltPhysicsDirectBodyState3D *get_direct_state();
+
+ PhysicsServer3D::BodyMode get_mode() const { return mode; }
+
+ void set_mode(PhysicsServer3D::BodyMode p_mode);
+
+ bool is_static() const { return mode == PhysicsServer3D::BODY_MODE_STATIC; }
+ bool is_kinematic() const { return mode == PhysicsServer3D::BODY_MODE_KINEMATIC; }
+ bool is_rigid_free() const { return mode == PhysicsServer3D::BODY_MODE_RIGID; }
+ bool is_rigid_linear() const { return mode == PhysicsServer3D::BODY_MODE_RIGID_LINEAR; }
+ bool is_rigid() const { return is_rigid_free() || is_rigid_linear(); }
+
+ bool is_ccd_enabled() const;
+ void set_ccd_enabled(bool p_enabled);
+
+ float get_mass() const { return mass; }
+ void set_mass(float p_mass);
+
+ Vector3 get_inertia() const { return inertia; }
+ void set_inertia(const Vector3 &p_inertia);
+
+ float get_bounce() const;
+ void set_bounce(float p_bounce);
+
+ float get_friction() const;
+ void set_friction(float p_friction);
+
+ float get_gravity_scale() const { return gravity_scale; }
+ void set_gravity_scale(float p_scale);
+
+ Vector3 get_gravity() const { return gravity; }
+
+ float get_linear_damp() const { return linear_damp; }
+ void set_linear_damp(float p_damp);
+
+ float get_angular_damp() const { return angular_damp; }
+ void set_angular_damp(float p_damp);
+
+ float get_total_linear_damp() const { return total_linear_damp; }
+ float get_total_angular_damp() const { return total_angular_damp; }
+
+ float get_collision_priority() const { return collision_priority; }
+ void set_collision_priority(float p_priority) { collision_priority = p_priority; }
+
+ DampMode get_linear_damp_mode() const { return linear_damp_mode; }
+ void set_linear_damp_mode(DampMode p_mode) { linear_damp_mode = p_mode; }
+
+ DampMode get_angular_damp_mode() const { return angular_damp_mode; }
+ void set_angular_damp_mode(DampMode p_mode) { angular_damp_mode = p_mode; }
+
+ bool is_axis_locked(PhysicsServer3D::BodyAxis p_axis) const;
+ void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_enabled);
+ bool are_axes_locked() const { return locked_axes != 0; }
+
+ 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;
+};
+
+#endif // JOLT_BODY_3D_H
diff --git a/modules/jolt_physics/objects/jolt_group_filter.cpp b/modules/jolt_physics/objects/jolt_group_filter.cpp
new file mode 100644
index 000000000000..b06ce8c66b5e
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_group_filter.cpp
@@ -0,0 +1,60 @@
+/**************************************************************************/
+/* jolt_group_filter.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_group_filter.h"
+
+#include "jolt_area_3d.h"
+#include "jolt_body_3d.h"
+#include "jolt_object_3d.h"
+
+bool JoltGroupFilter::CanCollide(const JPH::CollisionGroup &p_group1, const JPH::CollisionGroup &p_group2) const {
+ const JoltObject3D *object1 = decode_object(p_group1.GetGroupID(), p_group1.GetSubGroupID());
+ const JoltObject3D *object2 = decode_object(p_group2.GetGroupID(), p_group2.GetSubGroupID());
+ return object1->can_interact_with(*object2);
+}
+
+void JoltGroupFilter::encode_object(const JoltObject3D *p_object, JPH::CollisionGroup::GroupID &r_group_id, JPH::CollisionGroup::SubGroupID &r_sub_group_id) {
+ // Since group filters don't grant us access to the bodies or their user data we are instead forced use the
+ // collision group to carry the upper and lower bits of our pointer, which we can access and decode in `CanCollide`.
+ const uint64_t address = reinterpret_cast(p_object);
+ r_group_id = JPH::CollisionGroup::GroupID(address >> 32U);
+ r_sub_group_id = JPH::CollisionGroup::SubGroupID(address & 0xFFFFFFFFULL);
+}
+
+const JoltObject3D *JoltGroupFilter::decode_object(JPH::CollisionGroup::GroupID p_group_id, JPH::CollisionGroup::SubGroupID p_sub_group_id) {
+ const uint64_t upper_bits = (uint64_t)p_group_id << 32U;
+ const uint64_t lower_bits = (uint64_t)p_sub_group_id;
+ const uint64_t address = uint64_t(upper_bits | lower_bits);
+ return reinterpret_cast(address);
+}
+
+static_assert(sizeof(JoltObject3D *) <= 8, "Pointer size greater than expected.");
+static_assert(sizeof(JPH::CollisionGroup::GroupID) == 4, "Size of Jolt's collision group ID has changed.");
+static_assert(sizeof(JPH::CollisionGroup::SubGroupID) == 4, "Size of Jolt's collision sub-group ID has changed.");
diff --git a/modules/jolt_physics/objects/jolt_group_filter.h b/modules/jolt_physics/objects/jolt_group_filter.h
new file mode 100644
index 000000000000..cadffb83ecaa
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_group_filter.h
@@ -0,0 +1,51 @@
+/**************************************************************************/
+/* jolt_group_filter.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_GROUP_FILTER_H
+#define JOLT_GROUP_FILTER_H
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/CollisionGroup.h"
+#include "Jolt/Physics/Collision/GroupFilter.h"
+
+class JoltObject3D;
+
+class JoltGroupFilter final : public JPH::GroupFilter {
+ virtual bool CanCollide(const JPH::CollisionGroup &p_group1, const JPH::CollisionGroup &p_group2) const override;
+
+public:
+ inline static JoltGroupFilter *instance = nullptr;
+
+ static void encode_object(const JoltObject3D *p_object, JPH::CollisionGroup::GroupID &r_group_id, JPH::CollisionGroup::SubGroupID &r_sub_group_id);
+ static const JoltObject3D *decode_object(JPH::CollisionGroup::GroupID p_group_id, JPH::CollisionGroup::SubGroupID p_sub_group_id);
+};
+
+#endif // JOLT_GROUP_FILTER_H
diff --git a/modules/jolt_physics/objects/jolt_object_3d.cpp b/modules/jolt_physics/objects/jolt_object_3d.cpp
new file mode 100644
index 000000000000..4d4dd46992c2
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_object_3d.cpp
@@ -0,0 +1,151 @@
+/**************************************************************************/
+/* jolt_object_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_object_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../spaces/jolt_layers.h"
+#include "../spaces/jolt_space_3d.h"
+#include "jolt_group_filter.h"
+
+#include "core/error/error_macros.h"
+#include "core/object/object.h"
+
+void JoltObject3D::_remove_from_space() {
+ if (unlikely(jolt_id.IsInvalid())) {
+ return;
+ }
+
+ space->remove_body(jolt_id);
+
+ jolt_id = JPH::BodyID();
+}
+
+void JoltObject3D::_reset_space() {
+ ERR_FAIL_NULL(space);
+
+ _space_changing();
+ _remove_from_space();
+ _add_to_space();
+ _space_changed();
+}
+
+void JoltObject3D::_update_object_layer() {
+ if (!in_space()) {
+ return;
+ }
+
+ space->get_body_iface().SetObjectLayer(jolt_id, _get_object_layer());
+}
+
+void JoltObject3D::_collision_layer_changed() {
+ _update_object_layer();
+}
+
+void JoltObject3D::_collision_mask_changed() {
+ _update_object_layer();
+}
+
+JoltObject3D::JoltObject3D(ObjectType p_object_type) :
+ object_type(p_object_type) {
+}
+
+JoltObject3D::~JoltObject3D() = default;
+
+Object *JoltObject3D::get_instance() const {
+ return ObjectDB::get_instance(instance_id);
+}
+
+void JoltObject3D::set_space(JoltSpace3D *p_space) {
+ if (space == p_space) {
+ return;
+ }
+
+ _space_changing();
+
+ if (space != nullptr) {
+ _remove_from_space();
+ }
+
+ space = p_space;
+
+ if (space != nullptr) {
+ _add_to_space();
+ }
+
+ _space_changed();
+}
+
+void JoltObject3D::set_collision_layer(uint32_t p_layer) {
+ if (p_layer == collision_layer) {
+ return;
+ }
+
+ collision_layer = p_layer;
+
+ _collision_layer_changed();
+}
+
+void JoltObject3D::set_collision_mask(uint32_t p_mask) {
+ if (p_mask == collision_mask) {
+ return;
+ }
+
+ collision_mask = p_mask;
+
+ _collision_mask_changed();
+}
+
+bool JoltObject3D::can_collide_with(const JoltObject3D &p_other) const {
+ return (collision_mask & p_other.get_collision_layer()) != 0;
+}
+
+bool JoltObject3D::can_interact_with(const JoltObject3D &p_other) const {
+ if (const JoltBody3D *other_body = p_other.as_body()) {
+ return can_interact_with(*other_body);
+ } else if (const JoltArea3D *other_area = p_other.as_area()) {
+ return can_interact_with(*other_area);
+ } else if (const JoltSoftBody3D *other_soft_body = p_other.as_soft_body()) {
+ return can_interact_with(*other_soft_body);
+ } else {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled object type: '%d'. This should not happen. Please report this.", p_other.get_type()));
+ }
+}
+
+void JoltObject3D::pre_step(float p_step, JPH::Body &p_jolt_body) {
+}
+
+void JoltObject3D::post_step(float p_step, JPH::Body &p_jolt_body) {
+}
+
+String JoltObject3D::to_string() const {
+ Object *instance = get_instance();
+ return instance != nullptr ? instance->to_string() : "";
+}
diff --git a/modules/jolt_physics/objects/jolt_object_3d.h b/modules/jolt_physics/objects/jolt_object_3d.h
new file mode 100644
index 000000000000..6fa9cc79050d
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_object_3d.h
@@ -0,0 +1,157 @@
+/**************************************************************************/
+/* jolt_object_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_OBJECT_3D_H
+#define JOLT_OBJECT_3D_H
+
+#include "../shapes/jolt_shape_instance_3d.h"
+
+#include "core/math/vector3.h"
+#include "core/object/object.h"
+#include "core/string/ustring.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/rid.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Body/Body.h"
+#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
+#include "Jolt/Physics/Collision/ObjectLayer.h"
+
+class JoltArea3D;
+class JoltBody3D;
+class JoltShapedObject3D;
+class JoltShape3D;
+class JoltSoftBody3D;
+class JoltSpace3D;
+
+class JoltObject3D {
+public:
+ enum ObjectType : char {
+ OBJECT_TYPE_INVALID,
+ OBJECT_TYPE_BODY,
+ OBJECT_TYPE_SOFT_BODY,
+ OBJECT_TYPE_AREA,
+ };
+
+protected:
+ LocalVector shapes;
+
+ RID rid;
+ ObjectID instance_id;
+ JoltSpace3D *space = nullptr;
+ JPH::BodyID jolt_id;
+
+ uint32_t collision_layer = 1;
+ uint32_t collision_mask = 1;
+
+ ObjectType object_type = OBJECT_TYPE_INVALID;
+
+ bool pickable = false;
+
+ virtual JPH::BroadPhaseLayer _get_broad_phase_layer() const = 0;
+ virtual JPH::ObjectLayer _get_object_layer() const = 0;
+
+ virtual void _add_to_space() = 0;
+ virtual void _remove_from_space();
+
+ void _reset_space();
+
+ void _update_object_layer();
+
+ virtual void _collision_layer_changed();
+ virtual void _collision_mask_changed();
+
+ virtual void _space_changing() {}
+ virtual void _space_changed() {}
+
+public:
+ explicit JoltObject3D(ObjectType p_object_type);
+ virtual ~JoltObject3D() = 0;
+
+ ObjectType get_type() const { return object_type; }
+
+ bool is_body() const { return object_type == OBJECT_TYPE_BODY; }
+ bool is_soft_body() const { return object_type == OBJECT_TYPE_SOFT_BODY; }
+ bool is_area() const { return object_type == OBJECT_TYPE_AREA; }
+ bool is_shaped() const { return object_type != OBJECT_TYPE_SOFT_BODY; }
+
+ JoltShapedObject3D *as_shaped() { return is_shaped() ? reinterpret_cast(this) : nullptr; }
+ const JoltShapedObject3D *as_shaped() const { return is_shaped() ? reinterpret_cast(this) : nullptr; }
+
+ JoltBody3D *as_body() { return is_body() ? reinterpret_cast(this) : nullptr; }
+ const JoltBody3D *as_body() const { return is_body() ? reinterpret_cast(this) : nullptr; }
+
+ JoltSoftBody3D *as_soft_body() { return is_soft_body() ? reinterpret_cast(this) : nullptr; }
+ const JoltSoftBody3D *as_soft_body() const { return is_soft_body() ? reinterpret_cast(this) : nullptr; }
+
+ JoltArea3D *as_area() { return is_area() ? reinterpret_cast(this) : nullptr; }
+ const JoltArea3D *as_area() const { return is_area() ? reinterpret_cast(this) : nullptr; }
+
+ RID get_rid() const { return rid; }
+ void set_rid(const RID &p_rid) { rid = p_rid; }
+
+ ObjectID get_instance_id() const { return instance_id; }
+ void set_instance_id(ObjectID p_id) { instance_id = p_id; }
+ Object *get_instance() const;
+
+ JPH::BodyID get_jolt_id() const { return jolt_id; }
+
+ JoltSpace3D *get_space() const { return space; }
+ void set_space(JoltSpace3D *p_space);
+ bool in_space() const { return space != nullptr && !jolt_id.IsInvalid(); }
+
+ uint32_t get_collision_layer() const { return collision_layer; }
+ void set_collision_layer(uint32_t p_layer);
+
+ uint32_t get_collision_mask() const { return collision_mask; }
+ void set_collision_mask(uint32_t p_mask);
+
+ virtual Vector3 get_velocity_at_position(const Vector3 &p_position) const = 0;
+
+ bool is_pickable() const { return pickable; }
+ void set_pickable(bool p_enabled) { pickable = p_enabled; }
+
+ bool can_collide_with(const JoltObject3D &p_other) const;
+ bool can_interact_with(const JoltObject3D &p_other) const;
+
+ virtual bool can_interact_with(const JoltBody3D &p_other) const = 0;
+ virtual bool can_interact_with(const JoltSoftBody3D &p_other) const = 0;
+ virtual bool can_interact_with(const JoltArea3D &p_other) const = 0;
+
+ virtual bool reports_contacts() const = 0;
+
+ virtual void pre_step(float p_step, JPH::Body &p_jolt_body);
+ virtual void post_step(float p_step, JPH::Body &p_jolt_body);
+
+ String to_string() const;
+};
+
+#endif // JOLT_OBJECT_3D_H
diff --git a/modules/jolt_physics/objects/jolt_physics_direct_body_state_3d.cpp b/modules/jolt_physics/objects/jolt_physics_direct_body_state_3d.cpp
new file mode 100644
index 000000000000..1d5090a9c34f
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_physics_direct_body_state_3d.cpp
@@ -0,0 +1,247 @@
+/**************************************************************************/
+/* jolt_physics_direct_body_state_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_physics_direct_body_state_3d.h"
+
+#include "../spaces/jolt_physics_direct_space_state_3d.h"
+#include "../spaces/jolt_space_3d.h"
+#include "jolt_body_3d.h"
+
+#include "core/error/error_macros.h"
+
+JoltPhysicsDirectBodyState3D::JoltPhysicsDirectBodyState3D(JoltBody3D *p_body) :
+ body(p_body) {
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_total_gravity() const {
+ return body->get_gravity();
+}
+
+real_t JoltPhysicsDirectBodyState3D::get_total_angular_damp() const {
+ return (real_t)body->get_total_angular_damp();
+}
+
+real_t JoltPhysicsDirectBodyState3D::get_total_linear_damp() const {
+ return (real_t)body->get_total_linear_damp();
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_center_of_mass() const {
+ return body->get_center_of_mass_relative();
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_center_of_mass_local() const {
+ return body->get_center_of_mass_local();
+}
+
+Basis JoltPhysicsDirectBodyState3D::get_principal_inertia_axes() const {
+ return body->get_principal_inertia_axes();
+}
+
+real_t JoltPhysicsDirectBodyState3D::get_inverse_mass() const {
+ return 1.0 / body->get_mass();
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_inverse_inertia() const {
+ return body->get_inverse_inertia();
+}
+
+Basis JoltPhysicsDirectBodyState3D::get_inverse_inertia_tensor() const {
+ return body->get_inverse_inertia_tensor();
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_linear_velocity() const {
+ return body->get_linear_velocity();
+}
+
+void JoltPhysicsDirectBodyState3D::set_linear_velocity(const Vector3 &p_velocity) {
+ return body->set_linear_velocity(p_velocity);
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_angular_velocity() const {
+ return body->get_angular_velocity();
+}
+
+void JoltPhysicsDirectBodyState3D::set_angular_velocity(const Vector3 &p_velocity) {
+ return body->set_angular_velocity(p_velocity);
+}
+
+void JoltPhysicsDirectBodyState3D::set_transform(const Transform3D &p_transform) {
+ return body->set_transform(p_transform);
+}
+
+Transform3D JoltPhysicsDirectBodyState3D::get_transform() const {
+ return body->get_transform_scaled();
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_velocity_at_local_position(const Vector3 &p_local_position) const {
+ return body->get_velocity_at_position(body->get_position() + p_local_position);
+}
+
+void JoltPhysicsDirectBodyState3D::apply_central_impulse(const Vector3 &p_impulse) {
+ return body->apply_central_impulse(p_impulse);
+}
+
+void JoltPhysicsDirectBodyState3D::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) {
+ return body->apply_impulse(p_impulse, p_position);
+}
+
+void JoltPhysicsDirectBodyState3D::apply_torque_impulse(const Vector3 &p_impulse) {
+ return body->apply_torque_impulse(p_impulse);
+}
+
+void JoltPhysicsDirectBodyState3D::apply_central_force(const Vector3 &p_force) {
+ return body->apply_central_force(p_force);
+}
+
+void JoltPhysicsDirectBodyState3D::apply_force(const Vector3 &p_force, const Vector3 &p_position) {
+ return body->apply_force(p_force, p_position);
+}
+
+void JoltPhysicsDirectBodyState3D::apply_torque(const Vector3 &p_torque) {
+ return body->apply_torque(p_torque);
+}
+
+void JoltPhysicsDirectBodyState3D::add_constant_central_force(const Vector3 &p_force) {
+ return body->add_constant_central_force(p_force);
+}
+
+void JoltPhysicsDirectBodyState3D::add_constant_force(const Vector3 &p_force, const Vector3 &p_position) {
+ return body->add_constant_force(p_force, p_position);
+}
+
+void JoltPhysicsDirectBodyState3D::add_constant_torque(const Vector3 &p_torque) {
+ return body->add_constant_torque(p_torque);
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_constant_force() const {
+ return body->get_constant_force();
+}
+
+void JoltPhysicsDirectBodyState3D::set_constant_force(const Vector3 &p_force) {
+ return body->set_constant_force(p_force);
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_constant_torque() const {
+ return body->get_constant_torque();
+}
+
+void JoltPhysicsDirectBodyState3D::set_constant_torque(const Vector3 &p_torque) {
+ return body->set_constant_torque(p_torque);
+}
+
+bool JoltPhysicsDirectBodyState3D::is_sleeping() const {
+ return body->is_sleeping();
+}
+
+void JoltPhysicsDirectBodyState3D::set_sleep_state(bool p_enabled) {
+ body->set_is_sleeping(p_enabled);
+}
+
+int JoltPhysicsDirectBodyState3D::get_contact_count() const {
+ return body->get_contact_count();
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_contact_local_position(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), Vector3());
+ return body->get_contact(p_contact_idx).position;
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_contact_local_normal(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), Vector3());
+ return body->get_contact(p_contact_idx).normal;
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_contact_impulse(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), Vector3());
+ return body->get_contact(p_contact_idx).impulse;
+}
+
+int JoltPhysicsDirectBodyState3D::get_contact_local_shape(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), 0);
+ return body->get_contact(p_contact_idx).shape_index;
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_contact_local_velocity_at_position(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), Vector3());
+ return body->get_contact(p_contact_idx).velocity;
+}
+
+RID JoltPhysicsDirectBodyState3D::get_contact_collider(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), RID());
+ return body->get_contact(p_contact_idx).collider_rid;
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_contact_collider_position(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), Vector3());
+ return body->get_contact(p_contact_idx).collider_position;
+}
+
+ObjectID JoltPhysicsDirectBodyState3D::get_contact_collider_id(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), ObjectID());
+ return body->get_contact(p_contact_idx).collider_id;
+}
+
+Object *JoltPhysicsDirectBodyState3D::get_contact_collider_object(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), nullptr);
+ return ObjectDB::get_instance(body->get_contact(p_contact_idx).collider_id);
+}
+
+int JoltPhysicsDirectBodyState3D::get_contact_collider_shape(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), 0);
+ return body->get_contact(p_contact_idx).collider_shape_index;
+}
+
+Vector3 JoltPhysicsDirectBodyState3D::get_contact_collider_velocity_at_position(int p_contact_idx) const {
+ ERR_FAIL_INDEX_V(p_contact_idx, (int)body->get_contact_count(), Vector3());
+ return body->get_contact(p_contact_idx).collider_velocity;
+}
+
+real_t JoltPhysicsDirectBodyState3D::get_step() const {
+ return (real_t)body->get_space()->get_last_step();
+}
+
+void JoltPhysicsDirectBodyState3D::integrate_forces() {
+ const float step = (float)get_step();
+
+ Vector3 linear_velocity = get_linear_velocity();
+ Vector3 angular_velocity = get_angular_velocity();
+
+ linear_velocity *= MAX(1.0f - (float)get_total_linear_damp() * step, 0.0f);
+ angular_velocity *= MAX(1.0f - (float)get_total_angular_damp() * step, 0.0f);
+
+ linear_velocity += get_total_gravity() * step;
+
+ set_linear_velocity(linear_velocity);
+ set_angular_velocity(angular_velocity);
+}
+
+PhysicsDirectSpaceState3D *JoltPhysicsDirectBodyState3D::get_space_state() {
+ return body->get_space()->get_direct_state();
+}
diff --git a/modules/jolt_physics/objects/jolt_physics_direct_body_state_3d.h b/modules/jolt_physics/objects/jolt_physics_direct_body_state_3d.h
new file mode 100644
index 000000000000..e2a7ad78cb6a
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_physics_direct_body_state_3d.h
@@ -0,0 +1,116 @@
+/**************************************************************************/
+/* jolt_physics_direct_body_state_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_PHYSICS_DIRECT_BODY_STATE_3D_H
+#define JOLT_PHYSICS_DIRECT_BODY_STATE_3D_H
+
+#include "servers/physics_server_3d.h"
+
+class JoltBody3D;
+
+class JoltPhysicsDirectBodyState3D final : public PhysicsDirectBodyState3D {
+ GDCLASS(JoltPhysicsDirectBodyState3D, PhysicsDirectBodyState3D)
+
+ JoltBody3D *body = nullptr;
+
+ static void _bind_methods() {}
+
+public:
+ JoltPhysicsDirectBodyState3D() = default;
+
+ explicit JoltPhysicsDirectBodyState3D(JoltBody3D *p_body);
+
+ virtual Vector3 get_total_gravity() const override;
+ virtual real_t get_total_linear_damp() const override;
+ virtual real_t get_total_angular_damp() const override;
+
+ virtual Vector3 get_center_of_mass() const override;
+ virtual Vector3 get_center_of_mass_local() const override;
+ virtual Basis get_principal_inertia_axes() const override;
+
+ virtual real_t get_inverse_mass() const override;
+ virtual Vector3 get_inverse_inertia() const override;
+ virtual Basis get_inverse_inertia_tensor() const override;
+
+ virtual void set_linear_velocity(const Vector3 &p_velocity) override;
+ virtual Vector3 get_linear_velocity() const override;
+
+ virtual void set_angular_velocity(const Vector3 &p_velocity) override;
+ virtual Vector3 get_angular_velocity() const override;
+
+ virtual void set_transform(const Transform3D &p_transform) override;
+ virtual Transform3D get_transform() const override;
+
+ virtual Vector3 get_velocity_at_local_position(const Vector3 &p_local_position) const override;
+
+ virtual void apply_central_impulse(const Vector3 &p_impulse) override;
+ virtual void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) override;
+ virtual void apply_torque_impulse(const Vector3 &p_impulse) override;
+
+ virtual void apply_central_force(const Vector3 &p_force) override;
+ virtual void apply_force(const Vector3 &p_force, const Vector3 &p_position) override;
+ virtual void apply_torque(const Vector3 &p_torque) override;
+
+ virtual void add_constant_central_force(const Vector3 &p_force) override;
+ virtual void add_constant_force(const Vector3 &p_force, const Vector3 &p_position) override;
+ virtual void add_constant_torque(const Vector3 &p_torque) override;
+
+ virtual void set_constant_force(const Vector3 &p_force) override;
+ virtual Vector3 get_constant_force() const override;
+
+ virtual void set_constant_torque(const Vector3 &p_torque) override;
+ virtual Vector3 get_constant_torque() const override;
+
+ virtual void set_sleep_state(bool p_enabled) override;
+ virtual bool is_sleeping() const override;
+
+ virtual int get_contact_count() const override;
+
+ virtual Vector3 get_contact_local_position(int p_contact_idx) const override;
+ virtual Vector3 get_contact_local_normal(int p_contact_idx) const override;
+ virtual Vector3 get_contact_impulse(int p_contact_idx) const override;
+ virtual int get_contact_local_shape(int p_contact_idx) const override;
+ virtual Vector3 get_contact_local_velocity_at_position(int p_contact_idx) const override;
+
+ virtual RID get_contact_collider(int p_contact_idx) const override;
+ virtual Vector3 get_contact_collider_position(int p_contact_idx) const override;
+ virtual ObjectID get_contact_collider_id(int p_contact_idx) const override;
+ virtual Object *get_contact_collider_object(int p_contact_idx) const override;
+ virtual int get_contact_collider_shape(int p_contact_idx) const override;
+ virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const override;
+
+ virtual real_t get_step() const override;
+
+ virtual void integrate_forces() override;
+
+ virtual PhysicsDirectSpaceState3D *get_space_state() override;
+};
+
+#endif // JOLT_PHYSICS_DIRECT_BODY_STATE_3D_H
diff --git a/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp b/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
new file mode 100644
index 000000000000..e68f30348b8d
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
@@ -0,0 +1,433 @@
+/**************************************************************************/
+/* jolt_shaped_object_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_shaped_object_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "../shapes/jolt_custom_double_sided_shape.h"
+#include "../shapes/jolt_shape_3d.h"
+#include "../spaces/jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/EmptyShape.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() {
+ int built_shapes = 0;
+
+ for (JoltShapeInstance3D &shape : shapes) {
+ if (shape.is_enabled() && shape.try_build()) {
+ built_shapes += 1;
+ }
+ }
+
+ if (unlikely(built_shapes == 0)) {
+ return nullptr;
+ }
+
+ JPH::ShapeRefC result = built_shapes == 1 ? _try_build_single_shape() : _try_build_compound_shape();
+ if (unlikely(result == nullptr)) {
+ return nullptr;
+ }
+
+ if (has_custom_center_of_mass()) {
+ result = JoltShape3D::with_center_of_mass(result, get_center_of_mass_custom());
+ }
+
+ if (scale != Vector3(1, 1, 1)) {
+ Vector3 actual_scale = scale;
+ JOLT_ENSURE_SCALE_VALID(result, actual_scale, vformat("Failed to correctly scale body '%s'.", to_string()));
+ result = JoltShape3D::with_scale(result, actual_scale);
+ }
+
+ if (is_area()) {
+ result = JoltShape3D::with_double_sided(result, true);
+ }
+
+ return result;
+}
+
+JPH::ShapeRefC JoltShapedObject3D::_try_build_single_shape() {
+ for (int shape_index = 0; shape_index < (int)shapes.size(); ++shape_index) {
+ const JoltShapeInstance3D &sub_shape = shapes[shape_index];
+
+ if (!sub_shape.is_enabled() || !sub_shape.is_built()) {
+ continue;
+ }
+
+ JPH::ShapeRefC jolt_sub_shape = sub_shape.get_jolt_ref();
+
+ Vector3 sub_shape_scale = sub_shape.get_scale();
+ 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_sub_shape = JoltShape3D::with_scale(jolt_sub_shape, sub_shape_scale);
+ }
+
+ if (sub_shape_transform != Transform3D()) {
+ jolt_sub_shape = JoltShape3D::with_basis_origin(jolt_sub_shape, sub_shape_transform.basis, sub_shape_transform.origin);
+ }
+
+ return jolt_sub_shape;
+ }
+
+ return nullptr;
+}
+
+JPH::ShapeRefC JoltShapedObject3D::_try_build_compound_shape() {
+ JPH::StaticCompoundShapeSettings compound_shape_settings;
+
+ for (int shape_index = 0; shape_index < (int)shapes.size(); ++shape_index) {
+ const JoltShapeInstance3D &sub_shape = shapes[shape_index];
+
+ if (!sub_shape.is_enabled() || !sub_shape.is_built()) {
+ continue;
+ }
+
+ JPH::ShapeRefC jolt_sub_shape = sub_shape.get_jolt_ref();
+
+ Vector3 sub_shape_scale = sub_shape.get_scale();
+ 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_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);
+ }
+
+ 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())));
+
+ return shape_result.Get();
+}
+
+void JoltShapedObject3D::_shapes_changed() {
+ _update_shape();
+ _update_object_layer();
+}
+
+void JoltShapedObject3D::_space_changing() {
+ JoltObject3D::_space_changing();
+
+ if (space != nullptr) {
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ jolt_settings = new JPH::BodyCreationSettings(body->GetBodyCreationSettings());
+ }
+}
+
+void JoltShapedObject3D::_update_shape() {
+ if (!in_space()) {
+ _shapes_built();
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ previous_jolt_shape = jolt_shape;
+ jolt_shape = build_shape();
+
+ if (jolt_shape == previous_jolt_shape) {
+ return;
+ }
+
+ 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) {
+ jolt_settings->mAllowSleeping = true;
+ jolt_settings->mFriction = 1.0f;
+ jolt_settings->mRestitution = 0.0f;
+ jolt_settings->mLinearDamping = 0.0f;
+ jolt_settings->mAngularDamping = 0.0f;
+ jolt_settings->mGravityFactor = 0.0f;
+}
+
+JoltShapedObject3D::~JoltShapedObject3D() {
+ if (jolt_settings != nullptr) {
+ delete jolt_settings;
+ jolt_settings = nullptr;
+ }
+}
+
+Transform3D JoltShapedObject3D::get_transform_unscaled() const {
+ if (!in_space()) {
+ return { to_godot(jolt_settings->mRotation), to_godot(jolt_settings->mPosition) };
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Transform3D());
+
+ return { to_godot(body->GetRotation()), to_godot(body->GetPosition()) };
+}
+
+Transform3D JoltShapedObject3D::get_transform_scaled() const {
+ return get_transform_unscaled().scaled_local(scale);
+}
+
+Basis JoltShapedObject3D::get_basis() const {
+ if (!in_space()) {
+ return to_godot(jolt_settings->mRotation);
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Basis());
+
+ return to_godot(body->GetRotation());
+}
+
+Vector3 JoltShapedObject3D::get_position() const {
+ if (!in_space()) {
+ return to_godot(jolt_settings->mPosition);
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Vector3());
+
+ return to_godot(body->GetPosition());
+}
+
+Vector3 JoltShapedObject3D::get_center_of_mass() const {
+ ERR_FAIL_NULL_V_MSG(space, Vector3(), vformat("Failed to retrieve center-of-mass of '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Vector3());
+
+ return to_godot(body->GetCenterOfMassPosition());
+}
+
+Vector3 JoltShapedObject3D::get_center_of_mass_relative() const {
+ return get_center_of_mass() - get_position();
+}
+
+Vector3 JoltShapedObject3D::get_center_of_mass_local() const {
+ ERR_FAIL_NULL_V_MSG(space, Vector3(), vformat("Failed to retrieve local center-of-mass of '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ return get_transform_scaled().xform_inv(get_center_of_mass());
+}
+
+Vector3 JoltShapedObject3D::get_linear_velocity() const {
+ if (!in_space()) {
+ return to_godot(jolt_settings->mLinearVelocity);
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Vector3());
+
+ return to_godot(body->GetLinearVelocity());
+}
+
+Vector3 JoltShapedObject3D::get_angular_velocity() const {
+ if (!in_space()) {
+ return to_godot(jolt_settings->mAngularVelocity);
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Vector3());
+
+ return to_godot(body->GetAngularVelocity());
+}
+
+AABB JoltShapedObject3D::get_aabb() const {
+ AABB result;
+
+ for (const JoltShapeInstance3D &shape : shapes) {
+ if (shape.is_disabled()) {
+ continue;
+ }
+
+ if (result == AABB()) {
+ result = shape.get_aabb();
+ } else {
+ result.merge_with(shape.get_aabb());
+ }
+ }
+
+ return get_transform_scaled().xform(result);
+}
+
+JPH::ShapeRefC JoltShapedObject3D::build_shape() {
+ JPH::ShapeRefC new_shape = _try_build_shape();
+
+ if (new_shape == nullptr) {
+ if (has_custom_center_of_mass()) {
+ new_shape = JPH::EmptyShapeSettings(to_jolt(get_center_of_mass_custom())).Create().Get();
+ } else {
+ new_shape = new JPH::EmptyShape();
+ }
+ }
+
+ return new_shape;
+}
+
+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()));
+
+ shapes.push_back(JoltShapeInstance3D(this, p_shape, p_transform.orthonormalized(), p_transform.basis.get_scale(), p_disabled));
+
+ _shapes_changed();
+}
+
+void JoltShapedObject3D::remove_shape(const JoltShape3D *p_shape) {
+ for (int i = shapes.size() - 1; i >= 0; i--) {
+ if (shapes[i].get_shape() == p_shape) {
+ shapes.remove_at(i);
+ }
+ }
+
+ _shapes_changed();
+}
+
+void JoltShapedObject3D::remove_shape(int p_index) {
+ ERR_FAIL_INDEX(p_index, (int)shapes.size());
+ shapes.remove_at(p_index);
+
+ _shapes_changed();
+}
+
+JoltShape3D *JoltShapedObject3D::get_shape(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, (int)shapes.size(), nullptr);
+ return shapes[p_index].get_shape();
+}
+
+void JoltShapedObject3D::set_shape(int p_index, JoltShape3D *p_shape) {
+ ERR_FAIL_INDEX(p_index, (int)shapes.size());
+ shapes[p_index] = JoltShapeInstance3D(this, p_shape);
+
+ _shapes_changed();
+}
+
+void JoltShapedObject3D::clear_shapes() {
+ shapes.clear();
+
+ _shapes_changed();
+}
+
+int JoltShapedObject3D::find_shape_index(uint32_t p_shape_instance_id) const {
+ for (int i = 0; i < (int)shapes.size(); ++i) {
+ if (shapes[i].get_id() == p_shape_instance_id) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int JoltShapedObject3D::find_shape_index(const JPH::SubShapeID &p_sub_shape_id) const {
+ ERR_FAIL_NULL_V(jolt_shape, -1);
+ return find_shape_index((uint32_t)jolt_shape->GetSubShapeUserData(p_sub_shape_id));
+}
+
+JoltShape3D *JoltShapedObject3D::find_shape(uint32_t p_shape_instance_id) const {
+ const int shape_index = find_shape_index(p_shape_instance_id);
+ return shape_index != -1 ? shapes[shape_index].get_shape() : nullptr;
+}
+
+JoltShape3D *JoltShapedObject3D::find_shape(const JPH::SubShapeID &p_sub_shape_id) const {
+ const int shape_index = find_shape_index(p_sub_shape_id);
+ return shape_index != -1 ? shapes[shape_index].get_shape() : nullptr;
+}
+
+Transform3D JoltShapedObject3D::get_shape_transform_unscaled(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, (int)shapes.size(), Transform3D());
+ return shapes[p_index].get_transform_unscaled();
+}
+
+Transform3D JoltShapedObject3D::get_shape_transform_scaled(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, (int)shapes.size(), Transform3D());
+ return shapes[p_index].get_transform_scaled();
+}
+
+void JoltShapedObject3D::set_shape_transform(int p_index, Transform3D p_transform) {
+ ERR_FAIL_INDEX(p_index, (int)shapes.size());
+ JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, "Failed to correctly set transform for shape at index %d in body '%s'.");
+
+ Vector3 new_scale = p_transform.basis.get_scale();
+ p_transform.basis.orthonormalize();
+
+ JoltShapeInstance3D &shape = shapes[p_index];
+
+ if (shape.get_transform_unscaled() == p_transform && shape.get_scale() == new_scale) {
+ return;
+ }
+
+ shape.set_transform(p_transform);
+ shape.set_scale(new_scale);
+
+ _shapes_changed();
+}
+
+Vector3 JoltShapedObject3D::get_shape_scale(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, (int)shapes.size(), Vector3());
+ return shapes[p_index].get_scale();
+}
+
+bool JoltShapedObject3D::is_shape_disabled(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, (int)shapes.size(), false);
+ return shapes[p_index].is_disabled();
+}
+
+void JoltShapedObject3D::set_shape_disabled(int p_index, bool p_disabled) {
+ ERR_FAIL_INDEX(p_index, (int)shapes.size());
+
+ JoltShapeInstance3D &shape = shapes[p_index];
+
+ if (shape.is_disabled() == p_disabled) {
+ return;
+ }
+
+ if (p_disabled) {
+ shape.disable();
+ } else {
+ shape.enable();
+ }
+
+ _shapes_changed();
+}
+
+void JoltShapedObject3D::post_step(float p_step, JPH::Body &p_jolt_body) {
+ JoltObject3D::post_step(p_step, p_jolt_body);
+
+ previous_jolt_shape = nullptr;
+}
diff --git a/modules/jolt_physics/objects/jolt_shaped_object_3d.h b/modules/jolt_physics/objects/jolt_shaped_object_3d.h
new file mode 100644
index 000000000000..9632746287b9
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_shaped_object_3d.h
@@ -0,0 +1,123 @@
+/**************************************************************************/
+/* jolt_shaped_object_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_SHAPED_OBJECT_3D_H
+#define JOLT_SHAPED_OBJECT_3D_H
+
+#include "jolt_object_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Body/Body.h"
+#include "Jolt/Physics/Body/BodyCreationSettings.h"
+
+class JoltShapedObject3D : public JoltObject3D {
+ friend class JoltShape3D;
+
+protected:
+ Vector3 scale = Vector3(1, 1, 1);
+
+ JPH::ShapeRefC jolt_shape;
+ JPH::ShapeRefC previous_jolt_shape;
+
+ JPH::BodyCreationSettings *jolt_settings = new JPH::BodyCreationSettings();
+
+ virtual JPH::EMotionType _get_motion_type() const = 0;
+
+ bool _is_big() const;
+
+ JPH::ShapeRefC _try_build_shape();
+ JPH::ShapeRefC _try_build_single_shape();
+ JPH::ShapeRefC _try_build_compound_shape();
+
+ virtual void _shapes_changed();
+ virtual void _shapes_built() {}
+ virtual void _space_changing() override;
+
+ void _update_shape();
+
+public:
+ explicit JoltShapedObject3D(ObjectType p_object_type);
+ virtual ~JoltShapedObject3D() override;
+
+ Transform3D get_transform_unscaled() const;
+ Transform3D get_transform_scaled() const;
+
+ Vector3 get_scale() const { return scale; }
+ Basis get_basis() const;
+ Vector3 get_position() const;
+
+ Vector3 get_center_of_mass() const;
+ Vector3 get_center_of_mass_relative() const;
+ Vector3 get_center_of_mass_local() const;
+
+ Vector3 get_linear_velocity() const;
+ Vector3 get_angular_velocity() const;
+
+ AABB get_aabb() const;
+
+ virtual bool has_custom_center_of_mass() const = 0;
+ virtual Vector3 get_center_of_mass_custom() const = 0;
+
+ JPH::ShapeRefC build_shape();
+
+ const JPH::Shape *get_jolt_shape() const { return jolt_shape; }
+ const JPH::Shape *get_previous_jolt_shape() const { return previous_jolt_shape; }
+
+ void add_shape(JoltShape3D *p_shape, Transform3D p_transform, bool p_disabled);
+ void remove_shape(const JoltShape3D *p_shape);
+ void remove_shape(int p_index);
+
+ JoltShape3D *get_shape(int p_index) const;
+ void set_shape(int p_index, JoltShape3D *p_shape);
+
+ void clear_shapes();
+
+ int get_shape_count() const { return shapes.size(); }
+
+ int find_shape_index(uint32_t p_shape_instance_id) const;
+ int find_shape_index(const JPH::SubShapeID &p_sub_shape_id) const;
+
+ JoltShape3D *find_shape(uint32_t p_shape_instance_id) const;
+ JoltShape3D *find_shape(const JPH::SubShapeID &p_sub_shape_id) const;
+
+ Transform3D get_shape_transform_unscaled(int p_index) const;
+ Transform3D get_shape_transform_scaled(int p_index) const;
+ void set_shape_transform(int p_index, Transform3D p_transform);
+
+ Vector3 get_shape_scale(int p_index) const;
+
+ bool is_shape_disabled(int p_index) const;
+ void set_shape_disabled(int p_index, bool p_disabled);
+
+ virtual void post_step(float p_step, JPH::Body &p_jolt_body) override;
+};
+
+#endif // JOLT_SHAPED_OBJECT_3D_H
diff --git a/modules/jolt_physics/objects/jolt_soft_body_3d.cpp b/modules/jolt_physics/objects/jolt_soft_body_3d.cpp
new file mode 100644
index 000000000000..142eb10526c0
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_soft_body_3d.cpp
@@ -0,0 +1,733 @@
+/**************************************************************************/
+/* jolt_soft_body_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_soft_body_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+#include "../spaces/jolt_broad_phase_layer.h"
+#include "../spaces/jolt_space_3d.h"
+#include "jolt_area_3d.h"
+#include "jolt_body_3d.h"
+#include "jolt_group_filter.h"
+
+#include "core/error/error_macros.h"
+#include "servers/rendering_server.h"
+
+#include "Jolt/Physics/SoftBody/SoftBodyMotionProperties.h"
+
+namespace {
+
+bool is_face_degenerate(const int p_face[3]) {
+ return p_face[0] == p_face[1] || p_face[0] == p_face[2] || p_face[1] == p_face[2];
+}
+
+template
+void pin_vertices(const JoltSoftBody3D &p_body, const HashSet &p_pinned_vertices, const LocalVector &p_mesh_to_physics, JPH::Array &r_physics_vertices) {
+ const int mesh_vertex_count = p_mesh_to_physics.size();
+ const int physics_vertex_count = (int)r_physics_vertices.size();
+
+ for (int mesh_index : p_pinned_vertices) {
+ ERR_CONTINUE_MSG(mesh_index < 0 || mesh_index >= mesh_vertex_count, vformat("Index %d of pinned vertex in soft body '%s' is out of bounds. There are only %d vertices in the current mesh.", mesh_index, p_body.to_string(), mesh_vertex_count));
+
+ const int physics_index = p_mesh_to_physics[mesh_index];
+ ERR_CONTINUE_MSG(physics_index < 0 || physics_index >= physics_vertex_count, vformat("Index %d of pinned vertex in soft body '%s' is out of bounds. There are only %d vertices in the current mesh. This should not happen. Please report this.", physics_index, p_body.to_string(), physics_vertex_count));
+
+ r_physics_vertices[physics_index].mInvMass = 0.0f;
+ }
+}
+
+} // namespace
+
+JPH::BroadPhaseLayer JoltSoftBody3D::_get_broad_phase_layer() const {
+ return JoltBroadPhaseLayer::BODY_DYNAMIC;
+}
+
+JPH::ObjectLayer JoltSoftBody3D::_get_object_layer() const {
+ ERR_FAIL_NULL_V(space, 0);
+
+ return space->map_to_object_layer(_get_broad_phase_layer(), collision_layer, collision_mask);
+}
+
+void JoltSoftBody3D::_space_changing() {
+ JoltObject3D::_space_changing();
+
+ _deref_shared_data();
+
+ if (space != nullptr && !jolt_id.IsInvalid()) {
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ jolt_settings = new JPH::SoftBodyCreationSettings(body->GetSoftBodyCreationSettings());
+ jolt_settings->mSettings = nullptr;
+ }
+}
+
+void JoltSoftBody3D::_space_changed() {
+ JoltObject3D::_space_changed();
+
+ _update_mass();
+ _update_pressure();
+ _update_damping();
+ _update_simulation_precision();
+ _update_group_filter();
+}
+
+void JoltSoftBody3D::_add_to_space() {
+ if (unlikely(space == nullptr || !mesh.is_valid())) {
+ return;
+ }
+
+ const bool has_valid_shared = _ref_shared_data();
+ ERR_FAIL_COND(!has_valid_shared);
+
+ JPH::CollisionGroup::GroupID group_id = 0;
+ JPH::CollisionGroup::SubGroupID sub_group_id = 0;
+ JoltGroupFilter::encode_object(this, group_id, sub_group_id);
+
+ jolt_settings->mSettings = shared->settings;
+ jolt_settings->mUserData = reinterpret_cast(this);
+ jolt_settings->mObjectLayer = _get_object_layer();
+ jolt_settings->mCollisionGroup = JPH::CollisionGroup(nullptr, group_id, sub_group_id);
+ jolt_settings->mMaxLinearVelocity = JoltProjectSettings::get_max_linear_velocity();
+
+ const JPH::BodyID new_jolt_id = space->add_soft_body(*this, *jolt_settings);
+ if (!new_jolt_id.IsInvalid()) {
+ jolt_id = new_jolt_id;
+ }
+
+ delete jolt_settings;
+ jolt_settings = nullptr;
+}
+
+bool JoltSoftBody3D::_ref_shared_data() {
+ HashMap::Iterator iter_shared_data = mesh_to_shared.find(mesh);
+
+ if (iter_shared_data == mesh_to_shared.end()) {
+ RenderingServer *rendering = RenderingServer::get_singleton();
+
+ const Array mesh_data = rendering->mesh_surface_get_arrays(mesh, 0);
+ ERR_FAIL_COND_V(mesh_data.is_empty(), false);
+
+ const PackedInt32Array mesh_indices = mesh_data[RenderingServer::ARRAY_INDEX];
+ ERR_FAIL_COND_V(mesh_indices.is_empty(), false);
+
+ const PackedVector3Array mesh_vertices = mesh_data[RenderingServer::ARRAY_VERTEX];
+ ERR_FAIL_COND_V(mesh_vertices.is_empty(), false);
+
+ iter_shared_data = mesh_to_shared.insert(mesh, Shared());
+
+ LocalVector &mesh_to_physics = iter_shared_data->value.mesh_to_physics;
+
+ JPH::SoftBodySharedSettings &settings = *iter_shared_data->value.settings;
+ settings.mVertexRadius = JoltProjectSettings::get_soft_body_point_radius();
+
+ JPH::Array &physics_vertices = settings.mVertices;
+ JPH::Array &physics_faces = settings.mFaces;
+
+ HashMap vertex_to_physics;
+
+ const int mesh_vertex_count = mesh_vertices.size();
+ const int mesh_index_count = mesh_indices.size();
+
+ mesh_to_physics.resize(mesh_vertex_count);
+ physics_vertices.reserve(mesh_vertex_count);
+ vertex_to_physics.reserve(mesh_vertex_count);
+
+ int physics_index_count = 0;
+
+ for (int i = 0; i < mesh_index_count; i += 3) {
+ int physics_face[3];
+ int mesh_face[3];
+
+ for (int j = 0; j < 3; ++j) {
+ const int mesh_index = mesh_indices[i + j];
+ const Vector3 vertex = mesh_vertices[mesh_index];
+
+ HashMap::Iterator iter_physics_index = vertex_to_physics.find(vertex);
+
+ if (iter_physics_index == vertex_to_physics.end()) {
+ physics_vertices.emplace_back(JPH::Float3((float)vertex.x, (float)vertex.y, (float)vertex.z), JPH::Float3(0.0f, 0.0f, 0.0f), 1.0f);
+
+ iter_physics_index = vertex_to_physics.insert(vertex, physics_index_count++);
+ }
+
+ mesh_face[j] = mesh_index;
+ physics_face[j] = iter_physics_index->value;
+ mesh_to_physics[mesh_index] = iter_physics_index->value;
+ }
+
+ ERR_CONTINUE_MSG(is_face_degenerate(physics_face), vformat("Failed to append face to soft body '%s'. Face was found to be degenerate. Face consist of indices %d, %d and %d.", to_string(), mesh_face[0], mesh_face[1], mesh_face[2]));
+
+ // Jolt uses a different winding order, so we swap the indices to account for that.
+ physics_faces.emplace_back((JPH::uint32)physics_face[2], (JPH::uint32)physics_face[1], (JPH::uint32)physics_face[0]);
+ }
+
+ // Pin whatever pinned vertices we have currently. This is used during the `Optimize` call below to order the
+ // constraints. Note that it's fine if the pinned vertices change later, but that will reduce the effectiveness
+ // of the constraints a bit.
+ pin_vertices(*this, pinned_vertices, mesh_to_physics, physics_vertices);
+
+ // Since Godot's stiffness is input as a coefficient between 0 and 1, and Jolt uses actual stiffness for its
+ // edge constraints, we crudely map one to the other with an arbitrary constant.
+ const float stiffness = MAX(Math::pow(stiffness_coefficient, 3.0f) * 100000.0f, 0.000001f);
+ const float inverse_stiffness = 1.0f / stiffness;
+
+ JPH::SoftBodySharedSettings::VertexAttributes vertex_attrib;
+ vertex_attrib.mCompliance = vertex_attrib.mShearCompliance = inverse_stiffness;
+
+ settings.CreateConstraints(&vertex_attrib, 1, JPH::SoftBodySharedSettings::EBendType::None);
+ settings.Optimize();
+ } else {
+ iter_shared_data->value.ref_count++;
+ }
+
+ shared = &iter_shared_data->value;
+
+ return true;
+}
+
+void JoltSoftBody3D::_deref_shared_data() {
+ if (unlikely(shared == nullptr)) {
+ return;
+ }
+
+ HashMap::Iterator iter = mesh_to_shared.find(mesh);
+ if (unlikely(iter == mesh_to_shared.end())) {
+ return;
+ }
+
+ if (--iter->value.ref_count == 0) {
+ mesh_to_shared.remove(iter);
+ }
+
+ shared = nullptr;
+}
+
+void JoltSoftBody3D::_update_mass() {
+ if (!in_space()) {
+ return;
+ }
+
+ JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+
+ JPH::Array &physics_vertices = motion_properties.GetVertices();
+
+ const float inverse_vertex_mass = mass == 0.0f ? 1.0f : (float)physics_vertices.size() / mass;
+
+ for (JPH::SoftBodyVertex &vertex : physics_vertices) {
+ vertex.mInvMass = inverse_vertex_mass;
+ }
+
+ pin_vertices(*this, pinned_vertices, shared->mesh_to_physics, physics_vertices);
+}
+
+void JoltSoftBody3D::_update_pressure() {
+ if (!in_space()) {
+ jolt_settings->mPressure = pressure;
+ return;
+ }
+
+ JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+
+ motion_properties.SetPressure(pressure);
+}
+
+void JoltSoftBody3D::_update_damping() {
+ if (!in_space()) {
+ jolt_settings->mLinearDamping = linear_damping;
+ return;
+ }
+
+ JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+
+ motion_properties.SetLinearDamping(linear_damping);
+}
+
+void JoltSoftBody3D::_update_simulation_precision() {
+ if (!in_space()) {
+ jolt_settings->mNumIterations = (JPH::uint32)simulation_precision;
+ return;
+ }
+
+ JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+
+ motion_properties.SetNumIterations((JPH::uint32)simulation_precision);
+}
+
+void JoltSoftBody3D::_update_group_filter() {
+ JPH::GroupFilter *group_filter = !exceptions.is_empty() ? JoltGroupFilter::instance : nullptr;
+
+ if (!in_space()) {
+ jolt_settings->mCollisionGroup.SetGroupFilter(group_filter);
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->GetCollisionGroup().SetGroupFilter(group_filter);
+}
+
+void JoltSoftBody3D::_try_rebuild() {
+ if (space != nullptr) {
+ _reset_space();
+ }
+}
+
+void JoltSoftBody3D::_mesh_changed() {
+ _try_rebuild();
+}
+
+void JoltSoftBody3D::_simulation_precision_changed() {
+ wake_up();
+}
+
+void JoltSoftBody3D::_mass_changed() {
+ wake_up();
+}
+
+void JoltSoftBody3D::_pressure_changed() {
+ _update_pressure();
+ wake_up();
+}
+
+void JoltSoftBody3D::_damping_changed() {
+ _update_damping();
+ wake_up();
+}
+
+void JoltSoftBody3D::_pins_changed() {
+ _update_mass();
+ wake_up();
+}
+
+void JoltSoftBody3D::_vertices_changed() {
+ wake_up();
+}
+
+void JoltSoftBody3D::_exceptions_changed() {
+ _update_group_filter();
+}
+
+JoltSoftBody3D::JoltSoftBody3D() :
+ JoltObject3D(OBJECT_TYPE_SOFT_BODY) {
+ jolt_settings->mRestitution = 0.0f;
+ jolt_settings->mFriction = 1.0f;
+ jolt_settings->mUpdatePosition = false;
+ jolt_settings->mMakeRotationIdentity = false;
+}
+
+JoltSoftBody3D::~JoltSoftBody3D() {
+ if (jolt_settings != nullptr) {
+ delete jolt_settings;
+ jolt_settings = nullptr;
+ }
+}
+
+bool JoltSoftBody3D::in_space() const {
+ return JoltObject3D::in_space() && shared != nullptr;
+}
+
+void JoltSoftBody3D::add_collision_exception(const RID &p_excepted_body) {
+ exceptions.push_back(p_excepted_body);
+
+ _exceptions_changed();
+}
+
+void JoltSoftBody3D::remove_collision_exception(const RID &p_excepted_body) {
+ exceptions.erase(p_excepted_body);
+
+ _exceptions_changed();
+}
+
+bool JoltSoftBody3D::has_collision_exception(const RID &p_excepted_body) const {
+ return exceptions.find(p_excepted_body) >= 0;
+}
+
+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);
+}
+
+bool JoltSoftBody3D::can_interact_with(const JoltSoftBody3D &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);
+}
+
+bool JoltSoftBody3D::can_interact_with(const JoltArea3D &p_other) const {
+ return p_other.can_interact_with(*this);
+}
+
+Vector3 JoltSoftBody3D::get_velocity_at_position(const Vector3 &p_position) const {
+ return { 0.0f, 0.0f, 0.0f };
+}
+
+void JoltSoftBody3D::set_mesh(const RID &p_mesh) {
+ if (unlikely(mesh == p_mesh)) {
+ return;
+ }
+
+ _deref_shared_data();
+
+ mesh = p_mesh;
+
+ _mesh_changed();
+}
+
+bool JoltSoftBody3D::is_sleeping() const {
+ if (!in_space()) {
+ return false;
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), false);
+
+ return !body->IsActive();
+}
+
+void JoltSoftBody3D::set_is_sleeping(bool p_enabled) {
+ if (!in_space()) {
+ return;
+ }
+
+ JPH::BodyInterface &body_iface = space->get_body_iface();
+
+ if (p_enabled) {
+ body_iface.DeactivateBody(jolt_id);
+ } else {
+ body_iface.ActivateBody(jolt_id);
+ }
+}
+
+bool JoltSoftBody3D::can_sleep() const {
+ if (!in_space()) {
+ return true;
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), false);
+
+ return body->GetAllowSleeping();
+}
+
+void JoltSoftBody3D::set_can_sleep(bool p_enabled) {
+ if (!in_space()) {
+ return;
+ }
+
+ const JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ body->SetAllowSleeping(p_enabled);
+}
+
+void JoltSoftBody3D::set_simulation_precision(int p_precision) {
+ if (unlikely(simulation_precision == p_precision)) {
+ return;
+ }
+
+ simulation_precision = MAX(p_precision, 0);
+
+ _simulation_precision_changed();
+}
+
+void JoltSoftBody3D::set_mass(float p_mass) {
+ if (unlikely(mass == p_mass)) {
+ return;
+ }
+
+ mass = MAX(p_mass, 0.0f);
+
+ _mass_changed();
+}
+
+float JoltSoftBody3D::get_stiffness_coefficient() const {
+ return stiffness_coefficient;
+}
+
+void JoltSoftBody3D::set_stiffness_coefficient(float p_coefficient) {
+ stiffness_coefficient = CLAMP(p_coefficient, 0.0f, 1.0f);
+}
+
+void JoltSoftBody3D::set_pressure(float p_pressure) {
+ if (unlikely(pressure == p_pressure)) {
+ return;
+ }
+
+ pressure = MAX(p_pressure, 0.0f);
+
+ _pressure_changed();
+}
+
+void JoltSoftBody3D::set_linear_damping(float p_damping) {
+ if (unlikely(linear_damping == p_damping)) {
+ return;
+ }
+
+ linear_damping = MAX(p_damping, 0.0f);
+
+ _damping_changed();
+}
+
+float JoltSoftBody3D::get_drag() const {
+ // Drag is not a thing in Jolt, and not supported by Godot Physics either.
+ return 0.0f;
+}
+
+void JoltSoftBody3D::set_drag(float p_drag) {
+ // Drag is not a thing in Jolt, and not supported by Godot Physics either.
+}
+
+Variant JoltSoftBody3D::get_state(PhysicsServer3D::BodyState p_state) const {
+ switch (p_state) {
+ case PhysicsServer3D::BODY_STATE_TRANSFORM: {
+ return get_transform();
+ }
+ case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {
+ ERR_FAIL_V_MSG(Variant(), "Linear velocity is not supported for soft bodies.");
+ }
+ case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {
+ ERR_FAIL_V_MSG(Variant(), "Angular velocity is not supported for soft bodies.");
+ }
+ case PhysicsServer3D::BODY_STATE_SLEEPING: {
+ return is_sleeping();
+ }
+ case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
+ return can_sleep();
+ }
+ default: {
+ ERR_FAIL_V_MSG(Variant(), vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
+ }
+ }
+}
+
+void JoltSoftBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_value) {
+ switch (p_state) {
+ case PhysicsServer3D::BODY_STATE_TRANSFORM: {
+ set_transform(p_value);
+ } break;
+ case PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY: {
+ ERR_FAIL_MSG("Linear velocity is not supported for soft bodies.");
+ } break;
+ case PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY: {
+ ERR_FAIL_MSG("Angular velocity is not supported for soft bodies.");
+ } break;
+ case PhysicsServer3D::BODY_STATE_SLEEPING: {
+ set_is_sleeping(p_value);
+ } break;
+ case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
+ set_can_sleep(p_value);
+ } break;
+ default: {
+ ERR_FAIL_MSG(vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
+ } break;
+ }
+}
+
+Transform3D JoltSoftBody3D::get_transform() const {
+ // Since any transform gets baked into the vertices anyway we can just return identity here.
+ return Transform3D();
+}
+
+void JoltSoftBody3D::set_transform(const Transform3D &p_transform) {
+ ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to set transform for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ // For whatever reason this has to be interpreted as a relative global-space transform rather than an absolute one,
+ // because `SoftBody3D` will immediately upon entering the scene tree set itself to be top-level and also set its
+ // transform to be identity, while still expecting to stay in its original position.
+ //
+ // We also discard any scaling, since we have no way of scaling the actual edge lengths.
+ const JPH::Mat44 relative_transform = to_jolt(p_transform.orthonormalized());
+
+ JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+ JPH::Array &physics_vertices = motion_properties.GetVertices();
+
+ for (JPH::SoftBodyVertex &vertex : physics_vertices) {
+ vertex.mPosition = vertex.mPreviousPosition = relative_transform * vertex.mPosition;
+ vertex.mVelocity = JPH::Vec3::sZero();
+ }
+}
+
+AABB JoltSoftBody3D::get_bounds() const {
+ ERR_FAIL_COND_V_MSG(!in_space(), AABB(), vformat("Failed to retrieve world bounds of '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), AABB());
+
+ return to_godot(body->GetWorldSpaceBounds());
+}
+
+void JoltSoftBody3D::update_rendering_server(PhysicsServer3DRenderingServerHandler *p_rendering_server_handler) {
+ // Ideally we would emit an actual error here, but that would spam the logs to the point where the actual cause will be drowned out.
+ if (unlikely(!in_space())) {
+ return;
+ }
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ const JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+
+ typedef JPH::SoftBodyMotionProperties::Vertex SoftBodyVertex;
+ typedef JPH::SoftBodyMotionProperties::Face SoftBodyFace;
+
+ const JPH::Array &physics_vertices = motion_properties.GetVertices();
+ const JPH::Array &physics_faces = motion_properties.GetFaces();
+
+ const int physics_vertex_count = (int)physics_vertices.size();
+
+ normals.resize(physics_vertex_count);
+
+ for (const SoftBodyFace &physics_face : physics_faces) {
+ // Jolt uses a different winding order, so we swap the indices to account for that.
+
+ const uint32_t i0 = physics_face.mVertex[2];
+ const uint32_t i1 = physics_face.mVertex[1];
+ const uint32_t i2 = physics_face.mVertex[0];
+
+ const Vector3 v0 = to_godot(physics_vertices[i0].mPosition);
+ const Vector3 v1 = to_godot(physics_vertices[i1].mPosition);
+ const Vector3 v2 = to_godot(physics_vertices[i2].mPosition);
+
+ const Vector3 normal = (v2 - v0).cross(v1 - v0).normalized();
+
+ normals[i0] = normal;
+ normals[i1] = normal;
+ normals[i2] = normal;
+ }
+
+ const int mesh_vertex_count = shared->mesh_to_physics.size();
+
+ for (int i = 0; i < mesh_vertex_count; ++i) {
+ const int physics_index = shared->mesh_to_physics[i];
+
+ const Vector3 vertex = to_godot(physics_vertices[(size_t)physics_index].mPosition);
+ const Vector3 normal = normals[(uint32_t)physics_index];
+
+ p_rendering_server_handler->set_vertex(i, vertex);
+ p_rendering_server_handler->set_normal(i, normal);
+ }
+
+ p_rendering_server_handler->set_aabb(get_bounds());
+}
+
+Vector3 JoltSoftBody3D::get_vertex_position(int p_index) {
+ ERR_FAIL_COND_V_MSG(!in_space(), Vector3(), vformat("Failed to retrieve point position for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ ERR_FAIL_NULL_V(shared, Vector3());
+ ERR_FAIL_INDEX_V(p_index, (int)shared->mesh_to_physics.size(), Vector3());
+ const size_t physics_index = (size_t)shared->mesh_to_physics[p_index];
+
+ const JoltReadableBody3D body = space->read_body(jolt_id);
+ ERR_FAIL_COND_V(body.is_invalid(), Vector3());
+
+ const JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+ const JPH::Array &physics_vertices = motion_properties.GetVertices();
+ const JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];
+
+ return to_godot(body->GetCenterOfMassPosition() + physics_vertex.mPosition);
+}
+
+void JoltSoftBody3D::set_vertex_position(int p_index, const Vector3 &p_position) {
+ ERR_FAIL_COND_MSG(!in_space(), vformat("Failed to set point position for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ ERR_FAIL_NULL(shared);
+ ERR_FAIL_INDEX(p_index, (int)shared->mesh_to_physics.size());
+ const size_t physics_index = (size_t)shared->mesh_to_physics[p_index];
+
+ const float last_step = space->get_last_step();
+ if (unlikely(last_step == 0.0f)) {
+ return;
+ }
+
+ JoltWritableBody3D body = space->write_body(jolt_id);
+ ERR_FAIL_COND(body.is_invalid());
+
+ JPH::SoftBodyMotionProperties &motion_properties = static_cast(*body->GetMotionPropertiesUnchecked());
+
+ JPH::Array &physics_vertices = motion_properties.GetVertices();
+ JPH::SoftBodyVertex &physics_vertex = physics_vertices[physics_index];
+
+ const JPH::RVec3 center_of_mass = body->GetCenterOfMassPosition();
+ const JPH::Vec3 local_position = JPH::Vec3(to_jolt_r(p_position) - center_of_mass);
+ const JPH::Vec3 displacement = local_position - physics_vertex.mPosition;
+ const JPH::Vec3 velocity = displacement / last_step;
+
+ physics_vertex.mVelocity = velocity;
+
+ _vertices_changed();
+}
+
+void JoltSoftBody3D::pin_vertex(int p_index) {
+ pinned_vertices.insert(p_index);
+
+ _pins_changed();
+}
+
+void JoltSoftBody3D::unpin_vertex(int p_index) {
+ pinned_vertices.erase(p_index);
+
+ _pins_changed();
+}
+
+void JoltSoftBody3D::unpin_all_vertices() {
+ pinned_vertices.clear();
+
+ _pins_changed();
+}
+
+bool JoltSoftBody3D::is_vertex_pinned(int p_index) const {
+ ERR_FAIL_COND_V_MSG(!in_space(), false, vformat("Failed retrieve pin status of point for '%s'. Doing so without a physics space is not supported when using Jolt Physics. If this relates to a node, try adding the node to a scene tree first.", to_string()));
+
+ ERR_FAIL_NULL_V(shared, false);
+ ERR_FAIL_INDEX_V(p_index, (int)shared->mesh_to_physics.size(), false);
+ const int physics_index = shared->mesh_to_physics[p_index];
+
+ return pinned_vertices.has(physics_index);
+}
+
+String JoltSoftBody3D::to_string() const {
+ Object *instance = get_instance();
+ return instance != nullptr ? instance->to_string() : "";
+}
diff --git a/modules/jolt_physics/objects/jolt_soft_body_3d.h b/modules/jolt_physics/objects/jolt_soft_body_3d.h
new file mode 100644
index 000000000000..5a8a7ddf4c38
--- /dev/null
+++ b/modules/jolt_physics/objects/jolt_soft_body_3d.h
@@ -0,0 +1,175 @@
+/**************************************************************************/
+/* jolt_soft_body_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_SOFT_BODY_3D_H
+#define JOLT_SOFT_BODY_3D_H
+
+#include "jolt_object_3d.h"
+
+#include "core/variant/typed_array.h"
+#include "servers/physics_server_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/SoftBody/SoftBodyCreationSettings.h"
+#include "Jolt/Physics/SoftBody/SoftBodySharedSettings.h"
+
+class JoltSpace3D;
+
+class JoltSoftBody3D final : public JoltObject3D {
+ struct Shared {
+ LocalVector mesh_to_physics;
+ JPH::Ref settings = new JPH::SoftBodySharedSettings();
+ int ref_count = 1;
+ };
+
+ inline static HashMap mesh_to_shared;
+
+ HashSet pinned_vertices;
+ LocalVector exceptions;
+ LocalVector normals;
+
+ const Shared *shared = nullptr;
+
+ RID mesh;
+
+ JPH::SoftBodyCreationSettings *jolt_settings = new JPH::SoftBodyCreationSettings();
+
+ float mass = 0.0f;
+ float pressure = 0.0f;
+ float linear_damping = 0.01f;
+ float stiffness_coefficient = 0.5f;
+
+ int simulation_precision = 5;
+
+ virtual JPH::BroadPhaseLayer _get_broad_phase_layer() const override;
+ virtual JPH::ObjectLayer _get_object_layer() const override;
+
+ virtual void _space_changing() override;
+ virtual void _space_changed() override;
+
+ virtual void _add_to_space() override;
+
+ bool _ref_shared_data();
+ void _deref_shared_data();
+
+ void _update_mass();
+ void _update_pressure();
+ void _update_damping();
+ void _update_simulation_precision();
+ void _update_group_filter();
+
+ void _try_rebuild();
+
+ void _mesh_changed();
+ void _simulation_precision_changed();
+ void _mass_changed();
+ void _pressure_changed();
+ void _damping_changed();
+ void _pins_changed();
+ void _vertices_changed();
+ void _exceptions_changed();
+
+public:
+ JoltSoftBody3D();
+ virtual ~JoltSoftBody3D() override;
+
+ bool in_space() const;
+
+ void add_collision_exception(const RID &p_excepted_body);
+ void remove_collision_exception(const RID &p_excepted_body);
+ bool has_collision_exception(const RID &p_excepted_body) const;
+
+ const LocalVector &get_collision_exceptions() const { return exceptions; }
+
+ 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;
+
+ virtual bool reports_contacts() const override { return false; }
+
+ virtual Vector3 get_velocity_at_position(const Vector3 &p_position) const override;
+
+ void set_mesh(const RID &p_mesh);
+
+ bool is_pickable() const { return pickable; }
+ void set_pickable(bool p_enabled) { pickable = p_enabled; }
+
+ bool is_sleeping() const;
+ void set_is_sleeping(bool p_enabled);
+
+ bool can_sleep() const;
+ void set_can_sleep(bool p_enabled);
+
+ void put_to_sleep() { set_is_sleeping(true); }
+ void wake_up() { set_is_sleeping(false); }
+
+ int get_simulation_precision() const { return simulation_precision; }
+ void set_simulation_precision(int p_precision);
+
+ float get_mass() const { return mass; }
+ void set_mass(float p_mass);
+
+ float get_stiffness_coefficient() const;
+ void set_stiffness_coefficient(float p_coefficient);
+
+ float get_pressure() const { return pressure; }
+ void set_pressure(float p_pressure);
+
+ float get_linear_damping() const { return linear_damping; }
+ void set_linear_damping(float p_damping);
+
+ float get_drag() const;
+ void set_drag(float p_drag);
+
+ Variant get_state(PhysicsServer3D::BodyState p_state) const;
+ void set_state(PhysicsServer3D::BodyState p_state, const Variant &p_value);
+
+ Transform3D get_transform() const;
+ void set_transform(const Transform3D &p_transform);
+
+ AABB get_bounds() const;
+
+ void update_rendering_server(PhysicsServer3DRenderingServerHandler *p_rendering_server_handler);
+
+ Vector3 get_vertex_position(int p_index);
+ void set_vertex_position(int p_index, const Vector3 &p_position);
+
+ void pin_vertex(int p_index);
+ void unpin_vertex(int p_index);
+
+ void unpin_all_vertices();
+
+ bool is_vertex_pinned(int p_index) const;
+
+ String to_string() const;
+};
+
+#endif // JOLT_SOFT_BODY_3D_H
diff --git a/modules/jolt_physics/register_types.cpp b/modules/jolt_physics/register_types.cpp
new file mode 100644
index 000000000000..3f4588841c61
--- /dev/null
+++ b/modules/jolt_physics/register_types.cpp
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "register_types.h"
+
+#include "jolt_globals.h"
+#include "jolt_physics_server_3d.h"
+#include "jolt_project_settings.h"
+#include "objects/jolt_physics_direct_body_state_3d.h"
+#include "spaces/jolt_physics_direct_space_state_3d.h"
+
+#include "servers/physics_server_3d_wrap_mt.h"
+
+PhysicsServer3D *create_jolt_physics_server() {
+#ifdef THREADS_ENABLED
+ bool run_on_separate_thread = GLOBAL_GET("physics/3d/run_on_separate_thread");
+#else
+ bool run_on_separate_thread = false;
+#endif
+
+ JoltPhysicsServer3D *physics_server = memnew(JoltPhysicsServer3D(run_on_separate_thread));
+
+ return memnew(PhysicsServer3DWrapMT(physics_server, run_on_separate_thread));
+}
+
+void initialize_jolt_physics_module(ModuleInitializationLevel p_level) {
+ switch (p_level) {
+ case MODULE_INITIALIZATION_LEVEL_CORE: {
+ } break;
+ case MODULE_INITIALIZATION_LEVEL_SERVERS: {
+ jolt_initialize();
+ PhysicsServer3DManager::get_singleton()->register_server("JoltPhysics3D", callable_mp_static(&create_jolt_physics_server));
+ JoltProjectSettings::register_settings();
+ } break;
+ case MODULE_INITIALIZATION_LEVEL_SCENE: {
+ } break;
+ case MODULE_INITIALIZATION_LEVEL_EDITOR: {
+ } break;
+ }
+}
+
+void uninitialize_jolt_physics_module(ModuleInitializationLevel p_level) {
+ switch (p_level) {
+ case MODULE_INITIALIZATION_LEVEL_CORE: {
+ } break;
+ case MODULE_INITIALIZATION_LEVEL_SERVERS: {
+ jolt_deinitialize();
+ } break;
+ case MODULE_INITIALIZATION_LEVEL_SCENE: {
+ } break;
+ case MODULE_INITIALIZATION_LEVEL_EDITOR: {
+ } break;
+ }
+}
diff --git a/modules/jolt_physics/register_types.h b/modules/jolt_physics/register_types.h
new file mode 100644
index 000000000000..1be106d02ac1
--- /dev/null
+++ b/modules/jolt_physics/register_types.h
@@ -0,0 +1,39 @@
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_PHYSICS_REGISTER_TYPES_H
+#define JOLT_PHYSICS_REGISTER_TYPES_H
+
+#include "modules/register_module_types.h"
+
+void initialize_jolt_physics_module(ModuleInitializationLevel p_level);
+void uninitialize_jolt_physics_module(ModuleInitializationLevel p_level);
+
+#endif // JOLT_PHYSICS_REGISTER_TYPES_H
diff --git a/modules/jolt_physics/shapes/jolt_box_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_box_shape_3d.cpp
new file mode 100644
index 000000000000..122b7d2ff8e8
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_box_shape_3d.cpp
@@ -0,0 +1,85 @@
+/**************************************************************************/
+/* jolt_box_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_box_shape_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/BoxShape.h"
+
+JPH::ShapeRefC JoltBoxShape3D::_build() const {
+ const float min_half_extent = (float)half_extents[half_extents.min_axis_index()];
+ const float actual_margin = MIN(margin, min_half_extent * JoltProjectSettings::get_collision_margin_fraction());
+
+ const JPH::BoxShapeSettings shape_settings(to_jolt(half_extents), actual_margin);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics box shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+Variant JoltBoxShape3D::get_data() const {
+ return half_extents;
+}
+
+void JoltBoxShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::VECTOR3);
+
+ const Vector3 new_half_extents = p_data;
+ if (unlikely(new_half_extents == half_extents)) {
+ return;
+ }
+
+ half_extents = new_half_extents;
+
+ destroy();
+}
+
+void JoltBoxShape3D::set_margin(float p_margin) {
+ if (unlikely(margin == p_margin)) {
+ return;
+ }
+
+ margin = p_margin;
+
+ destroy();
+}
+
+String JoltBoxShape3D::to_string() const {
+ return vformat("{half_extents=%v margin=%f}", half_extents, margin);
+}
+
+AABB JoltBoxShape3D::get_aabb() const {
+ return AABB(-half_extents, half_extents * 2.0f);
+}
diff --git a/modules/jolt_physics/shapes/jolt_box_shape_3d.h b/modules/jolt_physics/shapes/jolt_box_shape_3d.h
new file mode 100644
index 000000000000..896caffefd9a
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_box_shape_3d.h
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* jolt_box_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_BOX_SHAPE_3D_H
+#define JOLT_BOX_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltBoxShape3D final : public JoltShape3D {
+ Vector3 half_extents;
+ float margin = 0.04f;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_BOX; }
+ virtual bool is_convex() const override { return true; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return margin; }
+ virtual void set_margin(float p_margin) override;
+
+ virtual AABB get_aabb() const override;
+
+ String to_string() const;
+};
+
+#endif // JOLT_BOX_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_capsule_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_capsule_shape_3d.cpp
new file mode 100644
index 000000000000..6659b60f590d
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_capsule_shape_3d.cpp
@@ -0,0 +1,92 @@
+/**************************************************************************/
+/* jolt_capsule_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_capsule_shape_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/CapsuleShape.h"
+
+JPH::ShapeRefC JoltCapsuleShape3D::_build() const {
+ ERR_FAIL_COND_V_MSG(radius <= 0.0f, nullptr, vformat("Failed to build Jolt Physics capsule shape with %s. Its radius must be greater than 0. This shape belongs to %s.", to_string(), _owners_to_string()));
+ ERR_FAIL_COND_V_MSG(height <= 0.0f, nullptr, vformat("Failed to build Jolt Physics capsule shape with %s. Its height must be greater than 0. This shape belongs to %s.", to_string(), _owners_to_string()));
+ ERR_FAIL_COND_V_MSG(height < radius * 2.0f, nullptr, vformat("Failed to build Jolt Physics capsule shape with %s. Its height must be at least double that of its radius. This shape belongs to %s.", to_string(), _owners_to_string()));
+
+ const float half_height = height / 2.0f;
+ const float cylinder_height = half_height - radius;
+
+ const JPH::CapsuleShapeSettings shape_settings(cylinder_height, radius);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics capsule shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+Variant JoltCapsuleShape3D::get_data() const {
+ Dictionary data;
+ data["height"] = height;
+ data["radius"] = radius;
+ return data;
+}
+
+void JoltCapsuleShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
+
+ const Dictionary data = p_data;
+
+ const Variant maybe_height = data.get("height", Variant());
+ ERR_FAIL_COND(maybe_height.get_type() != Variant::FLOAT);
+
+ const Variant maybe_radius = data.get("radius", Variant());
+ ERR_FAIL_COND(maybe_radius.get_type() != Variant::FLOAT);
+
+ const float new_height = maybe_height;
+ const float new_radius = maybe_radius;
+
+ if (unlikely(new_height == height && new_radius == radius)) {
+ return;
+ }
+
+ height = new_height;
+ radius = new_radius;
+
+ destroy();
+}
+
+AABB JoltCapsuleShape3D::get_aabb() const {
+ const Vector3 half_extents(radius, height / 2.0f, radius);
+ return AABB(-half_extents, half_extents * 2.0f);
+}
+
+String JoltCapsuleShape3D::to_string() const {
+ return vformat("{height=%f radius=%f}", height, radius);
+}
diff --git a/modules/jolt_physics/shapes/jolt_capsule_shape_3d.h b/modules/jolt_physics/shapes/jolt_capsule_shape_3d.h
new file mode 100644
index 000000000000..8e3f932e91ac
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_capsule_shape_3d.h
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* jolt_capsule_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CAPSULE_SHAPE_3D_H
+#define JOLT_CAPSULE_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltCapsuleShape3D final : public JoltShape3D {
+ float height = 0.0f;
+ float radius = 0.0f;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_CAPSULE; }
+ virtual bool is_convex() const override { return true; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return 0.0f; }
+ virtual void set_margin(float p_margin) override {}
+
+ virtual AABB get_aabb() const override;
+
+ String to_string() const;
+};
+
+#endif // JOLT_CAPSULE_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_concave_polygon_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_concave_polygon_shape_3d.cpp
new file mode 100644
index 000000000000..c11978d39e5c
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_concave_polygon_shape_3d.cpp
@@ -0,0 +1,127 @@
+/**************************************************************************/
+/* jolt_concave_polygon_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_concave_polygon_shape_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/MeshShape.h"
+
+JPH::ShapeRefC JoltConcavePolygonShape3D::_build() const {
+ const int vertex_count = (int)faces.size();
+ const int face_count = vertex_count / 3;
+ const int excess_vertex_count = vertex_count % 3;
+
+ if (unlikely(vertex_count == 0)) {
+ return nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(vertex_count < 3, nullptr, vformat("Failed to build Jolt Physics concave polygon shape with %s. It must have a vertex count of at least 3. This shape belongs to %s.", to_string(), _owners_to_string()));
+ ERR_FAIL_COND_V_MSG(excess_vertex_count != 0, nullptr, vformat("Failed to build Jolt Physics concave polygon shape with %s. It must have a vertex count that is divisible by 3. This shape belongs to %s.", to_string(), _owners_to_string()));
+
+ JPH::TriangleList jolt_faces;
+ jolt_faces.reserve((size_t)face_count);
+
+ const Vector3 *faces_begin = &faces[0];
+ const Vector3 *faces_end = faces_begin + vertex_count;
+ JPH::uint32 triangle_index = 0;
+
+ for (const Vector3 *vertex = faces_begin; vertex != faces_end; vertex += 3) {
+ const Vector3 *v0 = vertex + 0;
+ const Vector3 *v1 = vertex + 1;
+ const Vector3 *v2 = vertex + 2;
+
+ // Jolt uses a different winding order, so we swizzle the vertices to account for that.
+ jolt_faces.emplace_back(
+ JPH::Float3((float)v2->x, (float)v2->y, (float)v2->z),
+ JPH::Float3((float)v1->x, (float)v1->y, (float)v1->z),
+ JPH::Float3((float)v0->x, (float)v0->y, (float)v0->z),
+ 0,
+ triangle_index++);
+ }
+
+ JPH::MeshShapeSettings shape_settings(jolt_faces);
+ shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::get_active_edge_threshold();
+ shape_settings.mPerTriangleUserData = JoltProjectSettings::enable_ray_cast_face_index();
+
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics concave polygon shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return JoltShape3D::with_double_sided(shape_result.Get(), back_face_collision);
+}
+
+AABB JoltConcavePolygonShape3D::_calculate_aabb() const {
+ AABB result;
+
+ for (int i = 0; i < faces.size(); ++i) {
+ const Vector3 &vertex = faces[i];
+
+ if (i == 0) {
+ result.position = vertex;
+ } else {
+ result.expand_to(vertex);
+ }
+ }
+
+ return result;
+}
+
+Variant JoltConcavePolygonShape3D::get_data() const {
+ Dictionary data;
+ data["faces"] = faces;
+ data["backface_collision"] = back_face_collision;
+ return data;
+}
+
+void JoltConcavePolygonShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
+
+ const Dictionary data = p_data;
+
+ const Variant maybe_faces = data.get("faces", Variant());
+ ERR_FAIL_COND(maybe_faces.get_type() != Variant::PACKED_VECTOR3_ARRAY);
+
+ const Variant maybe_back_face_collision = data.get("backface_collision", Variant());
+ ERR_FAIL_COND(maybe_back_face_collision.get_type() != Variant::BOOL);
+
+ faces = maybe_faces;
+ back_face_collision = maybe_back_face_collision;
+
+ aabb = _calculate_aabb();
+
+ destroy();
+}
+
+String JoltConcavePolygonShape3D::to_string() const {
+ return vformat("{vertex_count=%d}", faces.size());
+}
diff --git a/modules/jolt_physics/shapes/jolt_concave_polygon_shape_3d.h b/modules/jolt_physics/shapes/jolt_concave_polygon_shape_3d.h
new file mode 100644
index 000000000000..d865c86aa72f
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_concave_polygon_shape_3d.h
@@ -0,0 +1,60 @@
+/**************************************************************************/
+/* jolt_concave_polygon_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CONCAVE_POLYGON_SHAPE_3D_H
+#define JOLT_CONCAVE_POLYGON_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltConcavePolygonShape3D final : public JoltShape3D {
+ AABB aabb;
+ PackedVector3Array faces;
+ bool back_face_collision = false;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+ AABB _calculate_aabb() const;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_CONCAVE_POLYGON; }
+ virtual bool is_convex() const override { return false; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return 0.0f; }
+ virtual void set_margin(float p_margin) override {}
+
+ virtual AABB get_aabb() const override { return aabb; }
+
+ String to_string() const;
+};
+
+#endif // JOLT_CONCAVE_POLYGON_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_convex_polygon_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_convex_polygon_shape_3d.cpp
new file mode 100644
index 000000000000..65d9ffe66df3
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_convex_polygon_shape_3d.cpp
@@ -0,0 +1,109 @@
+/**************************************************************************/
+/* jolt_convex_polygon_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_convex_polygon_shape_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/ConvexHullShape.h"
+
+JPH::ShapeRefC JoltConvexPolygonShape3D::_build() const {
+ const int vertex_count = (int)vertices.size();
+
+ if (unlikely(vertex_count == 0)) {
+ return nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(vertex_count < 3, nullptr, vformat("Failed to build Jolt Physics convex polygon shape with %s. It must have a vertex count of at least 3. This shape belongs to %s.", to_string(), _owners_to_string()));
+
+ JPH::Array jolt_vertices;
+ jolt_vertices.reserve((size_t)vertex_count);
+
+ const Vector3 *vertices_begin = &vertices[0];
+ const Vector3 *vertices_end = vertices_begin + vertex_count;
+
+ for (const Vector3 *vertex = vertices_begin; vertex != vertices_end; ++vertex) {
+ jolt_vertices.emplace_back((float)vertex->x, (float)vertex->y, (float)vertex->z);
+ }
+
+ const float min_half_extent = _calculate_aabb().get_shortest_axis_size() * 0.5f;
+ const float actual_margin = MIN(margin, min_half_extent * JoltProjectSettings::get_collision_margin_fraction());
+
+ const JPH::ConvexHullShapeSettings shape_settings(jolt_vertices, actual_margin);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics convex polygon shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+AABB JoltConvexPolygonShape3D::_calculate_aabb() const {
+ AABB result;
+
+ for (int i = 0; i < vertices.size(); ++i) {
+ if (i == 0) {
+ result.position = vertices[i];
+ } else {
+ result.expand_to(vertices[i]);
+ }
+ }
+
+ return result;
+}
+
+Variant JoltConvexPolygonShape3D::get_data() const {
+ return vertices;
+}
+
+void JoltConvexPolygonShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR3_ARRAY);
+
+ vertices = p_data;
+
+ aabb = _calculate_aabb();
+
+ destroy();
+}
+
+void JoltConvexPolygonShape3D::set_margin(float p_margin) {
+ if (unlikely(margin == p_margin)) {
+ return;
+ }
+
+ margin = p_margin;
+
+ destroy();
+}
+
+String JoltConvexPolygonShape3D::to_string() const {
+ return vformat("{vertex_count=%d margin=%f}", vertices.size(), margin);
+}
diff --git a/modules/jolt_physics/shapes/jolt_convex_polygon_shape_3d.h b/modules/jolt_physics/shapes/jolt_convex_polygon_shape_3d.h
new file mode 100644
index 000000000000..f623ce7599d3
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_convex_polygon_shape_3d.h
@@ -0,0 +1,60 @@
+/**************************************************************************/
+/* jolt_convex_polygon_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CONVEX_POLYGON_SHAPE_3D_H
+#define JOLT_CONVEX_POLYGON_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltConvexPolygonShape3D final : public JoltShape3D {
+ AABB aabb;
+ PackedVector3Array vertices;
+ float margin = 0.04f;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+ AABB _calculate_aabb() const;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_CONVEX_POLYGON; }
+ virtual bool is_convex() const override { return true; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return margin; }
+ virtual void set_margin(float p_margin) override;
+
+ virtual AABB get_aabb() const override { return aabb; }
+
+ String to_string() const;
+};
+
+#endif // JOLT_CONVEX_POLYGON_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_custom_decorated_shape.h b/modules/jolt_physics/shapes/jolt_custom_decorated_shape.h
new file mode 100644
index 000000000000..ac956f7b61f1
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_decorated_shape.h
@@ -0,0 +1,95 @@
+/**************************************************************************/
+/* jolt_custom_decorated_shape.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CUSTOM_DECORATED_SHAPE_H
+#define JOLT_CUSTOM_DECORATED_SHAPE_H
+
+#include "jolt_custom_shape_type.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/Shape/DecoratedShape.h"
+#include "Jolt/Physics/Collision/TransformedShape.h"
+
+class JoltCustomDecoratedShapeSettings : public JPH::DecoratedShapeSettings {
+public:
+ using JPH::DecoratedShapeSettings::DecoratedShapeSettings;
+};
+
+class JoltCustomDecoratedShape : public JPH::DecoratedShape {
+public:
+ using JPH::DecoratedShape::DecoratedShape;
+
+ virtual JPH::AABox GetLocalBounds() const override { return mInnerShape->GetLocalBounds(); }
+
+ virtual JPH::AABox GetWorldSpaceBounds(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale) const override { return mInnerShape->GetWorldSpaceBounds(p_center_of_mass_transform, p_scale); }
+
+ virtual float GetInnerRadius() const override { return mInnerShape->GetInnerRadius(); }
+
+ virtual JPH::MassProperties GetMassProperties() const override { return mInnerShape->GetMassProperties(); }
+
+ virtual JPH::Vec3 GetSurfaceNormal(const JPH::SubShapeID &p_sub_shape_id, JPH::Vec3Arg p_local_surface_position) const override { return mInnerShape->GetSurfaceNormal(p_sub_shape_id, p_local_surface_position); }
+
+ virtual JPH::uint64 GetSubShapeUserData(const JPH::SubShapeID &p_sub_shape_id) const override { return mInnerShape->GetSubShapeUserData(p_sub_shape_id); }
+
+ virtual JPH::TransformedShape GetSubShapeTransformedShape(const JPH::SubShapeID &p_sub_shape_id, JPH::Vec3Arg p_position_com, JPH::QuatArg p_rotation, JPH::Vec3Arg p_scale, JPH::SubShapeID &p_remainder) const override { return mInnerShape->GetSubShapeTransformedShape(p_sub_shape_id, p_position_com, p_rotation, p_scale, p_remainder); }
+
+ virtual void GetSubmergedVolume(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, const JPH::Plane &p_surface, float &p_total_volume, float &p_submerged_volume, JPH::Vec3 &p_center_of_buoyancy JPH_IF_DEBUG_RENDERER(, JPH::RVec3Arg p_base_offset)) const override { mInnerShape->GetSubmergedVolume(p_center_of_mass_transform, p_scale, p_surface, p_total_volume, p_submerged_volume, p_center_of_buoyancy JPH_IF_DEBUG_RENDERER(, p_base_offset)); }
+
+#ifdef JPH_DEBUG_RENDERER
+ virtual void Draw(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, JPH::ColorArg p_color, bool p_use_material_colors, bool p_draw_wireframe) const override { mInnerShape->Draw(p_renderer, p_center_of_mass_transform, p_scale, p_color, p_use_material_colors, p_draw_wireframe); }
+
+ virtual void DrawGetSupportFunction(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, JPH::ColorArg p_color, bool p_draw_support_direction) const override { mInnerShape->DrawGetSupportFunction(p_renderer, p_center_of_mass_transform, p_scale, p_color, p_draw_support_direction); }
+
+ virtual void DrawGetSupportingFace(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale) const override { mInnerShape->DrawGetSupportingFace(p_renderer, p_center_of_mass_transform, p_scale); }
+#endif
+
+ virtual bool CastRay(const JPH::RayCast &p_ray, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::RayCastResult &p_hit) const override { return mInnerShape->CastRay(p_ray, p_sub_shape_id_creator, p_hit); }
+
+ virtual void CastRay(const JPH::RayCast &p_ray, const JPH::RayCastSettings &p_ray_cast_settings, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CastRayCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override { return mInnerShape->CastRay(p_ray, p_ray_cast_settings, p_sub_shape_id_creator, p_collector, p_shape_filter); }
+
+ virtual void CollidePoint(JPH::Vec3Arg p_point, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CollidePointCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override { mInnerShape->CollidePoint(p_point, p_sub_shape_id_creator, p_collector, p_shape_filter); }
+
+ virtual void CollideSoftBodyVertices(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, const JPH::CollideSoftBodyVertexIterator &p_vertices, JPH::uint p_num_vertices, int p_colliding_shape_index) const override { mInnerShape->CollideSoftBodyVertices(p_center_of_mass_transform, p_scale, p_vertices, p_num_vertices, p_colliding_shape_index); }
+
+ virtual void CollectTransformedShapes(const JPH::AABox &p_box, JPH::Vec3Arg p_position_com, JPH::QuatArg p_rotation, JPH::Vec3Arg p_scale, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::TransformedShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override { mInnerShape->CollectTransformedShapes(p_box, p_position_com, p_rotation, p_scale, p_sub_shape_id_creator, p_collector, p_shape_filter); }
+
+ virtual void TransformShape(JPH::Mat44Arg p_center_of_mass_transform, JPH::TransformedShapeCollector &p_collector) const override { mInnerShape->TransformShape(p_center_of_mass_transform, p_collector); }
+
+ virtual void GetTrianglesStart(GetTrianglesContext &p_context, const JPH::AABox &p_box, JPH::Vec3Arg p_position_com, JPH::QuatArg p_rotation, JPH::Vec3Arg p_scale) const override { mInnerShape->GetTrianglesStart(p_context, p_box, p_position_com, p_rotation, p_scale); }
+
+ virtual int GetTrianglesNext(GetTrianglesContext &p_context, int p_max_triangles_requested, JPH::Float3 *p_triangle_vertices, const JPH::PhysicsMaterial **p_materials = nullptr) const override { return mInnerShape->GetTrianglesNext(p_context, p_max_triangles_requested, p_triangle_vertices, p_materials); }
+
+ virtual Stats GetStats() const override { return { sizeof(*this), 0 }; }
+
+ virtual float GetVolume() const override { return mInnerShape->GetVolume(); }
+};
+
+#endif // JOLT_CUSTOM_DECORATED_SHAPE_H
diff --git a/modules/jolt_physics/shapes/jolt_custom_double_sided_shape.cpp b/modules/jolt_physics/shapes/jolt_custom_double_sided_shape.cpp
new file mode 100644
index 000000000000..7511d8f2ac72
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_double_sided_shape.cpp
@@ -0,0 +1,113 @@
+/**************************************************************************/
+/* jolt_custom_double_sided_shape.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_custom_double_sided_shape.h"
+
+#include "../jolt_project_settings.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/CollisionDispatch.h"
+#include "Jolt/Physics/Collision/RayCast.h"
+
+namespace {
+
+JPH::Shape *construct_double_sided() {
+ return new JoltCustomDoubleSidedShape();
+}
+
+void collide_double_sided_vs_shape(const JPH::Shape *p_shape1, const JPH::Shape *p_shape2, JPH::Vec3Arg p_scale1, JPH::Vec3Arg p_scale2, JPH::Mat44Arg p_center_of_mass_transform1, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, const JPH::CollideShapeSettings &p_collide_shape_settings, JPH::CollideShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) {
+ ERR_FAIL_COND(p_shape1->GetSubType() != JoltCustomShapeSubType::DOUBLE_SIDED);
+
+ const JoltCustomDoubleSidedShape *shape1 = static_cast(p_shape1);
+
+ JPH::CollideShapeSettings new_collide_shape_settings = p_collide_shape_settings;
+ new_collide_shape_settings.mBackFaceMode = JPH::EBackFaceMode::CollideWithBackFaces;
+
+ JPH::CollisionDispatch::sCollideShapeVsShape(shape1->GetInnerShape(), p_shape2, p_scale1, p_scale2, p_center_of_mass_transform1, p_center_of_mass_transform2, p_sub_shape_id_creator1, p_sub_shape_id_creator2, new_collide_shape_settings, p_collector, p_shape_filter);
+}
+
+void collide_shape_vs_double_sided(const JPH::Shape *p_shape1, const JPH::Shape *p_shape2, JPH::Vec3Arg p_scale1, JPH::Vec3Arg p_scale2, JPH::Mat44Arg p_center_of_mass_transform1, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, const JPH::CollideShapeSettings &p_collide_shape_settings, JPH::CollideShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) {
+ ERR_FAIL_COND(p_shape2->GetSubType() != JoltCustomShapeSubType::DOUBLE_SIDED);
+
+ const JoltCustomDoubleSidedShape *shape2 = static_cast(p_shape2);
+
+ JPH::CollideShapeSettings new_collide_shape_settings = p_collide_shape_settings;
+ new_collide_shape_settings.mBackFaceMode = JPH::EBackFaceMode::CollideWithBackFaces;
+
+ JPH::CollisionDispatch::sCollideShapeVsShape(p_shape1, shape2->GetInnerShape(), p_scale1, p_scale2, p_center_of_mass_transform1, p_center_of_mass_transform2, p_sub_shape_id_creator1, p_sub_shape_id_creator2, new_collide_shape_settings, p_collector, p_shape_filter);
+}
+
+void cast_shape_vs_double_sided(const JPH::ShapeCast &p_shape_cast, const JPH::ShapeCastSettings &p_shape_cast_settings, const JPH::Shape *p_shape, JPH::Vec3Arg p_scale, const JPH::ShapeFilter &p_shape_filter, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, JPH::CastShapeCollector &p_collector) {
+ ERR_FAIL_COND(p_shape->GetSubType() != JoltCustomShapeSubType::DOUBLE_SIDED);
+
+ const auto *shape = static_cast(p_shape);
+
+ JPH::ShapeCastSettings new_shape_cast_settings = p_shape_cast_settings;
+ new_shape_cast_settings.mBackFaceModeTriangles = JPH::EBackFaceMode::CollideWithBackFaces;
+
+ JPH::CollisionDispatch::sCastShapeVsShapeLocalSpace(p_shape_cast, new_shape_cast_settings, shape->GetInnerShape(), p_scale, p_shape_filter, p_center_of_mass_transform2, p_sub_shape_id_creator1, p_sub_shape_id_creator2, p_collector);
+}
+
+} // namespace
+
+JPH::ShapeSettings::ShapeResult JoltCustomDoubleSidedShapeSettings::Create() const {
+ if (mCachedResult.IsEmpty()) {
+ new JoltCustomDoubleSidedShape(*this, mCachedResult);
+ }
+
+ return mCachedResult;
+}
+
+void JoltCustomDoubleSidedShape::register_type() {
+ JPH::ShapeFunctions &shape_functions = JPH::ShapeFunctions::sGet(JoltCustomShapeSubType::DOUBLE_SIDED);
+
+ shape_functions.mConstruct = construct_double_sided;
+ shape_functions.mColor = JPH::Color::sPurple;
+
+ for (const JPH::EShapeSubType sub_type : JPH::sAllSubShapeTypes) {
+ JPH::CollisionDispatch::sRegisterCollideShape(JoltCustomShapeSubType::DOUBLE_SIDED, sub_type, collide_double_sided_vs_shape);
+ JPH::CollisionDispatch::sRegisterCollideShape(sub_type, JoltCustomShapeSubType::DOUBLE_SIDED, collide_shape_vs_double_sided);
+ }
+
+ for (const JPH::EShapeSubType sub_type : JPH::sConvexSubShapeTypes) {
+ JPH::CollisionDispatch::sRegisterCastShape(sub_type, JoltCustomShapeSubType::DOUBLE_SIDED, cast_shape_vs_double_sided);
+ }
+}
+
+void JoltCustomDoubleSidedShape::CastRay(const JPH::RayCast &p_ray, const JPH::RayCastSettings &p_ray_cast_settings, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CastRayCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) const {
+ JPH::RayCastSettings new_ray_cast_settings = p_ray_cast_settings;
+
+ if (!back_face_collision) {
+ new_ray_cast_settings.SetBackFaceMode(JPH::EBackFaceMode::IgnoreBackFaces);
+ }
+
+ return mInnerShape->CastRay(p_ray, new_ray_cast_settings, p_sub_shape_id_creator, p_collector, p_shape_filter);
+}
diff --git a/modules/jolt_physics/shapes/jolt_custom_double_sided_shape.h b/modules/jolt_physics/shapes/jolt_custom_double_sided_shape.h
new file mode 100644
index 000000000000..664b633a2129
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_double_sided_shape.h
@@ -0,0 +1,74 @@
+/**************************************************************************/
+/* jolt_custom_double_sided_shape.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CUSTOM_DOUBLE_SIDED_SHAPE_H
+#define JOLT_CUSTOM_DOUBLE_SIDED_SHAPE_H
+
+#include "jolt_custom_decorated_shape.h"
+#include "jolt_custom_shape_type.h"
+
+class JoltCustomDoubleSidedShapeSettings final : public JoltCustomDecoratedShapeSettings {
+public:
+ bool back_face_collision = false;
+
+ JoltCustomDoubleSidedShapeSettings() = default;
+
+ JoltCustomDoubleSidedShapeSettings(const ShapeSettings *p_inner_settings, bool p_back_face_collision) :
+ JoltCustomDecoratedShapeSettings(p_inner_settings), back_face_collision(p_back_face_collision) {}
+
+ JoltCustomDoubleSidedShapeSettings(const JPH::Shape *p_inner_shape, bool p_back_face_collision) :
+ JoltCustomDecoratedShapeSettings(p_inner_shape), back_face_collision(p_back_face_collision) {}
+
+ virtual JPH::Shape::ShapeResult Create() const override;
+};
+
+class JoltCustomDoubleSidedShape final : public JoltCustomDecoratedShape {
+ bool back_face_collision = false;
+
+public:
+ static void register_type();
+
+ JoltCustomDoubleSidedShape() :
+ JoltCustomDecoratedShape(JoltCustomShapeSubType::DOUBLE_SIDED) {}
+
+ JoltCustomDoubleSidedShape(const JoltCustomDoubleSidedShapeSettings &p_settings, JPH::Shape::ShapeResult &p_result) :
+ JoltCustomDecoratedShape(JoltCustomShapeSubType::DOUBLE_SIDED, p_settings, p_result), back_face_collision(p_settings.back_face_collision) {
+ if (!p_result.HasError()) {
+ p_result.Set(this);
+ }
+ }
+
+ JoltCustomDoubleSidedShape(const JPH::Shape *p_inner_shape, bool p_back_face_collision) :
+ JoltCustomDecoratedShape(JoltCustomShapeSubType::DOUBLE_SIDED, p_inner_shape), back_face_collision(p_back_face_collision) {}
+
+ virtual void CastRay(const JPH::RayCast &p_ray, const JPH::RayCastSettings &p_ray_cast_settings, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CastRayCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override;
+};
+
+#endif // JOLT_CUSTOM_DOUBLE_SIDED_SHAPE_H
diff --git a/modules/jolt_physics/shapes/jolt_custom_motion_shape.cpp b/modules/jolt_physics/shapes/jolt_custom_motion_shape.cpp
new file mode 100644
index 000000000000..c0cd63833701
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_motion_shape.cpp
@@ -0,0 +1,78 @@
+/**************************************************************************/
+/* jolt_custom_motion_shape.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_custom_motion_shape.h"
+
+namespace {
+
+class JoltMotionConvexSupport final : public JPH::ConvexShape::Support {
+public:
+ JoltMotionConvexSupport(JPH::Vec3Arg p_motion, const JPH::ConvexShape::Support *p_inner_support) :
+ motion(p_motion),
+ inner_support(p_inner_support) {}
+
+ virtual JPH::Vec3 GetSupport(JPH::Vec3Arg p_direction) const override {
+ JPH::Vec3 support = inner_support->GetSupport(p_direction);
+
+ if (p_direction.Dot(motion) > 0) {
+ support += motion;
+ }
+
+ return support;
+ }
+
+ virtual float GetConvexRadius() const override { return inner_support->GetConvexRadius(); }
+
+private:
+ JPH::Vec3 motion = JPH::Vec3::sZero();
+
+ const JPH::ConvexShape::Support *inner_support = nullptr;
+};
+
+} // namespace
+
+JPH::AABox JoltCustomMotionShape::GetLocalBounds() const {
+ JPH::AABox aabb = inner_shape.GetLocalBounds();
+ JPH::AABox aabb_translated = aabb;
+ aabb_translated.Translate(motion);
+ aabb.Encapsulate(aabb_translated);
+
+ return aabb;
+}
+
+void JoltCustomMotionShape::GetSupportingFace(const JPH::SubShapeID &p_sub_shape_id, JPH::Vec3Arg p_direction, JPH::Vec3Arg p_scale, JPH::Mat44Arg p_center_of_mass_transform, JPH::Shape::SupportingFace &p_vertices) const {
+ // This is technically called when using the enhanced internal edge removal, but `JPH::InternalEdgeRemovingCollector` will
+ // only ever use the faces of the second shape in the collision pair, and this shape will always be the first in the pair, so
+ // we can safely skip this.
+}
+
+const JPH::ConvexShape::Support *JoltCustomMotionShape::GetSupportFunction(JPH::ConvexShape::ESupportMode p_mode, JPH::ConvexShape::SupportBuffer &p_buffer, JPH::Vec3Arg p_scale) const {
+ return new (&p_buffer) JoltMotionConvexSupport(motion, inner_shape.GetSupportFunction(p_mode, inner_support_buffer, p_scale));
+}
diff --git a/modules/jolt_physics/shapes/jolt_custom_motion_shape.h b/modules/jolt_physics/shapes/jolt_custom_motion_shape.h
new file mode 100644
index 000000000000..52a907c010e5
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_motion_shape.h
@@ -0,0 +1,117 @@
+/**************************************************************************/
+/* jolt_custom_motion_shape.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CUSTOM_MOTION_SHAPE_H
+#define JOLT_CUSTOM_MOTION_SHAPE_H
+
+#include "jolt_custom_shape_type.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/Shape/ConvexShape.h"
+#include "Jolt/Physics/Collision/TransformedShape.h"
+
+class JoltCustomMotionShape final : public JPH::ConvexShape {
+ mutable JPH::ConvexShape::SupportBuffer inner_support_buffer;
+
+ JPH::Vec3 motion = JPH::Vec3::sZero();
+
+ const JPH::ConvexShape &inner_shape;
+
+public:
+ explicit JoltCustomMotionShape(const JPH::ConvexShape &p_shape) :
+ JPH::ConvexShape(JoltCustomShapeSubType::MOTION), inner_shape(p_shape) {}
+
+ virtual bool MustBeStatic() const override { return false; }
+
+ virtual JPH::Vec3 GetCenterOfMass() const override { ERR_FAIL_V_MSG(JPH::Vec3::sZero(), "Not implemented."); }
+
+ virtual JPH::AABox GetLocalBounds() const override;
+
+ virtual JPH::uint GetSubShapeIDBitsRecursive() const override { ERR_FAIL_V_MSG(0, "Not implemented."); }
+
+ virtual JPH::AABox GetWorldSpaceBounds(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale) const override { ERR_FAIL_V_MSG(JPH::AABox(), "Not implemented."); }
+
+ virtual float GetInnerRadius() const override { ERR_FAIL_V_MSG(0.0f, "Not implemented."); }
+
+ virtual JPH::MassProperties GetMassProperties() const override { ERR_FAIL_V_MSG(JPH::MassProperties(), "Not implemented."); }
+
+ virtual const JPH::PhysicsMaterial *GetMaterial(const JPH::SubShapeID &p_sub_shape_id) const override { ERR_FAIL_V_MSG(nullptr, "Not implemented."); }
+
+ virtual JPH::Vec3 GetSurfaceNormal(const JPH::SubShapeID &p_sub_shape_id, JPH::Vec3Arg p_local_surface_position) const override { ERR_FAIL_V_MSG(JPH::Vec3::sZero(), "Not implemented."); }
+
+ virtual void GetSupportingFace(const JPH::SubShapeID &p_sub_shape_id, JPH::Vec3Arg p_direction, JPH::Vec3Arg p_scale, JPH::Mat44Arg p_center_of_mass_transform, JPH::Shape::SupportingFace &p_vertices) const override;
+
+ virtual JPH::uint64 GetSubShapeUserData(const JPH::SubShapeID &p_sub_shape_id) const override { ERR_FAIL_V_MSG(0, "Not implemented."); }
+
+ virtual JPH::TransformedShape GetSubShapeTransformedShape(const JPH::SubShapeID &p_sub_shape_id, JPH::Vec3Arg p_position_com, JPH::QuatArg p_rotation, JPH::Vec3Arg p_scale, JPH::SubShapeID &p_remainder) const override { ERR_FAIL_V_MSG(JPH::TransformedShape(), "Not implemented."); }
+
+ virtual void GetSubmergedVolume(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, const JPH::Plane &p_surface, float &p_total_volume, float &p_submerged_volume, JPH::Vec3 &p_center_of_buoyancy JPH_IF_DEBUG_RENDERER(, JPH::RVec3Arg p_base_offset)) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual const JPH::ConvexShape::Support *GetSupportFunction(JPH::ConvexShape::ESupportMode p_mode, JPH::ConvexShape::SupportBuffer &p_buffer, JPH::Vec3Arg p_scale) const override;
+
+#ifdef JPH_DEBUG_RENDERER
+ virtual void Draw(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, JPH::ColorArg p_color, bool p_use_material_colors, bool p_draw_wireframe) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual void DrawGetSupportFunction(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, JPH::ColorArg p_color, bool p_draw_support_direction) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual void DrawGetSupportingFace(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale) const override { ERR_FAIL_MSG("Not implemented."); }
+#endif
+
+ virtual bool CastRay(const JPH::RayCast &p_ray, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::RayCastResult &p_hit) const override { ERR_FAIL_V_MSG(false, "Not implemented."); }
+
+ virtual void CastRay(const JPH::RayCast &p_ray, const JPH::RayCastSettings &p_ray_cast_settings, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CastRayCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual void CollidePoint(JPH::Vec3Arg p_point, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CollidePointCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual void CollideSoftBodyVertices(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, const JPH::CollideSoftBodyVertexIterator &p_vertices, JPH::uint p_num_vertices, int p_colliding_shape_index) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual void CollectTransformedShapes(const JPH::AABox &p_box, JPH::Vec3Arg p_position_com, JPH::QuatArg p_rotation, JPH::Vec3Arg p_scale, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::TransformedShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual void TransformShape(JPH::Mat44Arg p_center_of_mass_transform, JPH::TransformedShapeCollector &p_collector) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual void GetTrianglesStart(GetTrianglesContext &p_context, const JPH::AABox &p_box, JPH::Vec3Arg p_position_com, JPH::QuatArg p_rotation, JPH::Vec3Arg p_scale) const override { ERR_FAIL_MSG("Not implemented."); }
+
+ virtual int GetTrianglesNext(GetTrianglesContext &p_context, int p_max_triangles_requested, JPH::Float3 *p_triangle_vertices, const JPH::PhysicsMaterial **p_materials = nullptr) const override { ERR_FAIL_V_MSG(0, "Not implemented."); }
+
+ virtual JPH::Shape::Stats GetStats() const override { return { sizeof(*this), 0 }; }
+
+ virtual float GetVolume() const override { ERR_FAIL_V_MSG(0.0f, "Not implemented."); }
+
+ virtual bool IsValidScale(JPH::Vec3Arg p_scale) const override { ERR_FAIL_V_MSG(false, "Not implemented."); }
+
+ const JPH::ConvexShape &get_inner_shape() const { return inner_shape; }
+
+ void set_motion(JPH::Vec3Arg p_motion) { motion = p_motion; }
+};
+
+#endif // JOLT_CUSTOM_MOTION_SHAPE_H
diff --git a/modules/jolt_physics/shapes/jolt_custom_ray_shape.cpp b/modules/jolt_physics/shapes/jolt_custom_ray_shape.cpp
new file mode 100644
index 000000000000..92ab10df73dc
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_ray_shape.cpp
@@ -0,0 +1,234 @@
+/**************************************************************************/
+/* jolt_custom_ray_shape.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_custom_ray_shape.h"
+
+#include "../spaces/jolt_query_collectors.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/CastResult.h"
+#include "Jolt/Physics/Collision/RayCast.h"
+#include "Jolt/Physics/Collision/TransformedShape.h"
+
+#ifdef JPH_DEBUG_RENDERER
+#include "Jolt/Renderer/DebugRenderer.h"
+#endif
+
+namespace {
+
+class JoltCustomRayShapeSupport final : public JPH::ConvexShape::Support {
+public:
+ explicit JoltCustomRayShapeSupport(float p_length) :
+ length(p_length) {}
+
+ virtual JPH::Vec3 GetSupport(JPH::Vec3Arg p_direction) const override {
+ if (p_direction.GetZ() > 0.0f) {
+ return { 0.0f, 0.0f, length };
+ } else {
+ return JPH::Vec3::sZero();
+ }
+ }
+
+ virtual float GetConvexRadius() const override { return 0.0f; }
+
+private:
+ float length = 0.0f;
+};
+
+static_assert(sizeof(JoltCustomRayShapeSupport) <= sizeof(JPH::ConvexShape::SupportBuffer), "Size of SeparationRayShape3D support is larger than size of support buffer.");
+
+JPH::Shape *construct_ray() {
+ return new JoltCustomRayShape();
+}
+
+void collide_ray_vs_shape(const JPH::Shape *p_shape1, const JPH::Shape *p_shape2, JPH::Vec3Arg p_scale1, JPH::Vec3Arg p_scale2, JPH::Mat44Arg p_center_of_mass_transform1, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, const JPH::CollideShapeSettings &p_collide_shape_settings, JPH::CollideShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) {
+ ERR_FAIL_COND(p_shape1->GetSubType() != JoltCustomShapeSubType::RAY);
+
+ const JoltCustomRayShape *shape1 = static_cast(p_shape1);
+
+ const float margin = p_collide_shape_settings.mMaxSeparationDistance;
+ const float ray_length = shape1->length;
+ const float ray_length_padded = ray_length + margin;
+
+ const JPH::Mat44 transform1 = p_center_of_mass_transform1 * JPH::Mat44::sScale(p_scale1);
+ const JPH::Mat44 transform2 = p_center_of_mass_transform2 * JPH::Mat44::sScale(p_scale2);
+ const JPH::Mat44 transform_inv2 = transform2.Inversed();
+
+ const JPH::Vec3 ray_start = transform1.GetTranslation();
+ const JPH::Vec3 ray_direction = transform1.GetAxisZ();
+ const JPH::Vec3 ray_vector = ray_direction * ray_length;
+ const JPH::Vec3 ray_vector_padded = ray_direction * ray_length_padded;
+
+ const JPH::Vec3 ray_start2 = transform_inv2 * ray_start;
+ const JPH::Vec3 ray_direction2 = transform_inv2.Multiply3x3(ray_direction);
+ const JPH::Vec3 ray_vector_padded2 = transform_inv2.Multiply3x3(ray_vector_padded);
+
+ const JPH::RayCast ray_cast(ray_start2, ray_vector_padded2);
+
+ JPH::RayCastSettings ray_cast_settings;
+ ray_cast_settings.mTreatConvexAsSolid = false;
+ ray_cast_settings.mBackFaceModeTriangles = p_collide_shape_settings.mBackFaceMode;
+
+ JoltQueryCollectorClosest ray_collector;
+
+ p_shape2->CastRay(ray_cast, ray_cast_settings, p_sub_shape_id_creator2, ray_collector);
+
+ if (!ray_collector.had_hit()) {
+ return;
+ }
+
+ const JPH::RayCastResult &hit = ray_collector.get_hit();
+
+ const float hit_distance = ray_length_padded * hit.mFraction;
+ const float hit_depth = ray_length - hit_distance;
+
+ if (-hit_depth >= p_collector.GetEarlyOutFraction()) {
+ return;
+ }
+
+ // Since `hit.mSubShapeID2` could represent a path not only from `p_shape2` but also any
+ // compound shape that it's contained within, we need to split this path into something that
+ // `p_shape2` can actually understand.
+ JPH::SubShapeID local_sub_shape_id2;
+ hit.mSubShapeID2.PopID(p_sub_shape_id_creator2.GetNumBitsWritten(), local_sub_shape_id2);
+
+ const JPH::Vec3 hit_point2 = ray_cast.GetPointOnRay(hit.mFraction);
+
+ const JPH::Vec3 hit_point_on_1 = ray_start + ray_vector;
+ const JPH::Vec3 hit_point_on_2 = transform2 * hit_point2;
+
+ JPH::Vec3 hit_normal2 = JPH::Vec3::sZero();
+
+ if (shape1->slide_on_slope) {
+ hit_normal2 = p_shape2->GetSurfaceNormal(local_sub_shape_id2, hit_point2);
+
+ // If we got a back-face normal we need to flip it.
+ if (hit_normal2.Dot(ray_direction2) > 0) {
+ hit_normal2 = -hit_normal2;
+ }
+ } else {
+ hit_normal2 = -ray_direction2;
+ }
+
+ const JPH::Vec3 hit_normal = transform2.Multiply3x3(hit_normal2);
+
+ JPH::CollideShapeResult result(hit_point_on_1, hit_point_on_2, -hit_normal, hit_depth, p_sub_shape_id_creator1.GetID(), hit.mSubShapeID2, JPH::TransformedShape::sGetBodyID(p_collector.GetContext()));
+
+ if (p_collide_shape_settings.mCollectFacesMode == JPH::ECollectFacesMode::CollectFaces) {
+ p_shape2->GetSupportingFace(local_sub_shape_id2, ray_direction2, p_scale2, p_center_of_mass_transform2, result.mShape2Face);
+ }
+
+ p_collector.AddHit(result);
+}
+
+void collide_noop(const JPH::Shape *p_shape1, const JPH::Shape *p_shape2, JPH::Vec3Arg p_scale1, JPH::Vec3Arg p_scale2, JPH::Mat44Arg p_center_of_mass_transform1, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, const JPH::CollideShapeSettings &p_collide_shape_settings, JPH::CollideShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) {
+}
+
+void cast_noop(const JPH::ShapeCast &p_shape_cast, const JPH::ShapeCastSettings &p_shape_cast_settings, const JPH::Shape *p_shape, JPH::Vec3Arg p_scale, const JPH::ShapeFilter &p_shape_filter, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, JPH::CastShapeCollector &p_collector) {
+}
+
+} // namespace
+
+JPH::ShapeSettings::ShapeResult JoltCustomRayShapeSettings::Create() const {
+ if (mCachedResult.IsEmpty()) {
+ new JoltCustomRayShape(*this, mCachedResult);
+ }
+
+ return mCachedResult;
+}
+
+void JoltCustomRayShape::register_type() {
+ JPH::ShapeFunctions &shape_functions = JPH::ShapeFunctions::sGet(JoltCustomShapeSubType::RAY);
+
+ shape_functions.mConstruct = construct_ray;
+ shape_functions.mColor = JPH::Color::sDarkRed;
+
+ static constexpr JPH::EShapeSubType concrete_sub_types[] = {
+ JPH::EShapeSubType::Sphere,
+ JPH::EShapeSubType::Box,
+ JPH::EShapeSubType::Triangle,
+ JPH::EShapeSubType::Capsule,
+ JPH::EShapeSubType::TaperedCapsule,
+ JPH::EShapeSubType::Cylinder,
+ JPH::EShapeSubType::ConvexHull,
+ JPH::EShapeSubType::Mesh,
+ JPH::EShapeSubType::HeightField,
+ JPH::EShapeSubType::Plane,
+ JPH::EShapeSubType::TaperedCylinder
+ };
+
+ for (const JPH::EShapeSubType concrete_sub_type : concrete_sub_types) {
+ JPH::CollisionDispatch::sRegisterCollideShape(JoltCustomShapeSubType::RAY, concrete_sub_type, collide_ray_vs_shape);
+ JPH::CollisionDispatch::sRegisterCollideShape(concrete_sub_type, JoltCustomShapeSubType::RAY, JPH::CollisionDispatch::sReversedCollideShape);
+ }
+
+ JPH::CollisionDispatch::sRegisterCollideShape(JoltCustomShapeSubType::RAY, JoltCustomShapeSubType::RAY, collide_noop);
+
+ for (const JPH::EShapeSubType sub_type : JPH::sAllSubShapeTypes) {
+ JPH::CollisionDispatch::sRegisterCastShape(JoltCustomShapeSubType::RAY, sub_type, cast_noop);
+ JPH::CollisionDispatch::sRegisterCastShape(sub_type, JoltCustomShapeSubType::RAY, cast_noop);
+ }
+}
+
+JPH::AABox JoltCustomRayShape::GetLocalBounds() const {
+ const float radius = GetInnerRadius();
+ return { JPH::Vec3(-radius, -radius, 0.0f), JPH::Vec3(radius, radius, length) };
+}
+
+float JoltCustomRayShape::GetInnerRadius() const {
+ // There is no sensible value here, since this shape is infinitely thin, so we pick something
+ // that's hopefully small enough to effectively be zero, but big enough to not cause any
+ // numerical issues.
+ return 0.0001f;
+}
+
+JPH::MassProperties JoltCustomRayShape::GetMassProperties() const {
+ JPH::MassProperties mass_properties;
+
+ // Since this shape has no volume we can't really give it a correct set of mass properties, so
+ // instead we just give it some arbitrary ones.
+ mass_properties.mMass = 1.0f;
+ mass_properties.mInertia = JPH::Mat44::sIdentity();
+
+ return mass_properties;
+}
+
+#ifdef JPH_DEBUG_RENDERER
+
+void JoltCustomRayShape::Draw(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, JPH::ColorArg p_color, bool p_use_material_colors, bool p_draw_wireframe) const {
+ p_renderer->DrawArrow(p_center_of_mass_transform.GetTranslation(), p_center_of_mass_transform * JPH::Vec3(0, 0, length * p_scale.GetZ()), p_use_material_colors ? GetMaterial()->GetDebugColor() : p_color, 0.1f);
+}
+
+#endif
+
+const JPH::ConvexShape::Support *JoltCustomRayShape::GetSupportFunction(JPH::ConvexShape::ESupportMode p_mode, JPH::ConvexShape::SupportBuffer &p_buffer, JPH::Vec3Arg p_scale) const {
+ return new (&p_buffer) JoltCustomRayShapeSupport(p_scale.GetZ() * length);
+}
diff --git a/modules/jolt_physics/shapes/jolt_custom_ray_shape.h b/modules/jolt_physics/shapes/jolt_custom_ray_shape.h
new file mode 100644
index 000000000000..30da52f17008
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_ray_shape.h
@@ -0,0 +1,107 @@
+/**************************************************************************/
+/* jolt_custom_ray_shape.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CUSTOM_RAY_SHAPE_H
+#define JOLT_CUSTOM_RAY_SHAPE_H
+
+#include "jolt_custom_shape_type.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/Shape/ConvexShape.h"
+
+class JoltCustomRayShapeSettings final : public JPH::ConvexShapeSettings {
+public:
+ JPH::RefConst material;
+ float length = 1.0f;
+ bool slide_on_slope = false;
+
+ JoltCustomRayShapeSettings() = default;
+ JoltCustomRayShapeSettings(float p_length, bool p_slide_on_slope, const JPH::PhysicsMaterial *p_material = nullptr) :
+ material(p_material), length(p_length), slide_on_slope(p_slide_on_slope) {}
+
+ virtual JPH::ShapeSettings::ShapeResult Create() const override;
+};
+
+class JoltCustomRayShape final : public JPH::ConvexShape {
+public:
+ JPH::RefConst material;
+ float length = 0.0f;
+ bool slide_on_slope = false;
+
+ static void register_type();
+
+ JoltCustomRayShape() :
+ JPH::ConvexShape(JoltCustomShapeSubType::RAY) {}
+
+ JoltCustomRayShape(const JoltCustomRayShapeSettings &p_settings, JPH::Shape::ShapeResult &p_result) :
+ JPH::ConvexShape(JoltCustomShapeSubType::RAY, p_settings, p_result), material(p_settings.material), length(p_settings.length), slide_on_slope(p_settings.slide_on_slope) {
+ if (!p_result.HasError()) {
+ p_result.Set(this);
+ }
+ }
+
+ JoltCustomRayShape(float p_length, bool p_slide_on_slope, const JPH::PhysicsMaterial *p_material = nullptr) :
+ JPH::ConvexShape(JoltCustomShapeSubType::RAY), material(p_material), length(p_length), slide_on_slope(p_slide_on_slope) {}
+
+ virtual JPH::AABox GetLocalBounds() const override;
+
+ virtual float GetInnerRadius() const override;
+
+ virtual JPH::MassProperties GetMassProperties() const override;
+
+ virtual JPH::Vec3 GetSurfaceNormal(const JPH::SubShapeID &p_sub_shape_id, JPH::Vec3Arg p_local_surface_position) const override { return JPH::Vec3::sAxisZ(); }
+
+ virtual void GetSubmergedVolume(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, const JPH::Plane &p_surface, float &p_total_volume, float &p_submerged_volume, JPH::Vec3 &p_center_of_buoyancy JPH_IF_DEBUG_RENDERER(, JPH::RVec3Arg p_base_offset)) const override {
+ p_total_volume = 0.0f;
+ p_submerged_volume = 0.0f;
+ p_center_of_buoyancy = JPH::Vec3::sZero();
+ }
+
+#ifdef JPH_DEBUG_RENDERER
+ virtual void Draw(JPH::DebugRenderer *p_renderer, JPH::RMat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, JPH::ColorArg p_color, bool p_use_material_colors, bool p_draw_wireframe) const override;
+#endif
+
+ virtual bool CastRay(const JPH::RayCast &p_ray, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::RayCastResult &p_hit) const override { return false; }
+
+ virtual void CastRay(const JPH::RayCast &p_ray, const JPH::RayCastSettings &p_ray_cast_settings, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CastRayCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override {}
+
+ virtual void CollidePoint(JPH::Vec3Arg p_point, const JPH::SubShapeIDCreator &p_sub_shape_id_creator, JPH::CollidePointCollector &p_collector, const JPH::ShapeFilter &p_shape_filter = JPH::ShapeFilter()) const override {}
+
+ virtual void CollideSoftBodyVertices(JPH::Mat44Arg p_center_of_mass_transform, JPH::Vec3Arg p_scale, const JPH::CollideSoftBodyVertexIterator &p_vertices, JPH::uint p_num_vertices, int p_colliding_shape_index) const override {}
+
+ virtual JPH::Shape::Stats GetStats() const override { return { sizeof(*this), 0 }; }
+
+ virtual float GetVolume() const override { return 0.0f; }
+
+ virtual const JPH::ConvexShape::Support *GetSupportFunction(JPH::ConvexShape::ESupportMode p_mode, JPH::ConvexShape::SupportBuffer &p_buffer, JPH::Vec3Arg p_scale) const override;
+};
+
+#endif // JOLT_CUSTOM_RAY_SHAPE_H
diff --git a/modules/jolt_physics/shapes/jolt_custom_shape_type.h b/modules/jolt_physics/shapes/jolt_custom_shape_type.h
new file mode 100644
index 000000000000..77371640c2a3
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_shape_type.h
@@ -0,0 +1,47 @@
+/**************************************************************************/
+/* jolt_custom_shape_type.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CUSTOM_SHAPE_TYPE_H
+#define JOLT_CUSTOM_SHAPE_TYPE_H
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/Shape/Shape.h"
+
+namespace JoltCustomShapeSubType {
+
+constexpr JPH::EShapeSubType OVERRIDE_USER_DATA = JPH::EShapeSubType::User1;
+constexpr JPH::EShapeSubType DOUBLE_SIDED = JPH::EShapeSubType::User2;
+constexpr JPH::EShapeSubType RAY = JPH::EShapeSubType::UserConvex1;
+constexpr JPH::EShapeSubType MOTION = JPH::EShapeSubType::UserConvex2;
+
+} // namespace JoltCustomShapeSubType
+
+#endif // JOLT_CUSTOM_SHAPE_TYPE_H
diff --git a/modules/jolt_physics/shapes/jolt_custom_user_data_shape.cpp b/modules/jolt_physics/shapes/jolt_custom_user_data_shape.cpp
new file mode 100644
index 000000000000..53637ebe6322
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_user_data_shape.cpp
@@ -0,0 +1,99 @@
+/**************************************************************************/
+/* jolt_custom_user_data_shape.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_custom_user_data_shape.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/CollisionDispatch.h"
+#include "Jolt/Physics/Collision/ShapeCast.h"
+
+namespace {
+
+JPH::Shape *construct_override_user_data() {
+ return new JoltCustomUserDataShape();
+}
+
+void collide_override_user_data_vs_shape(const JPH::Shape *p_shape1, const JPH::Shape *p_shape2, JPH::Vec3Arg p_scale1, JPH::Vec3Arg p_scale2, JPH::Mat44Arg p_center_of_mass_transform1, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, const JPH::CollideShapeSettings &p_collide_shape_settings, JPH::CollideShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) {
+ ERR_FAIL_COND(p_shape1->GetSubType() != JoltCustomShapeSubType::OVERRIDE_USER_DATA);
+
+ const JoltCustomUserDataShape *shape1 = static_cast(p_shape1);
+
+ JPH::CollisionDispatch::sCollideShapeVsShape(shape1->GetInnerShape(), p_shape2, p_scale1, p_scale2, p_center_of_mass_transform1, p_center_of_mass_transform2, p_sub_shape_id_creator1, p_sub_shape_id_creator2, p_collide_shape_settings, p_collector, p_shape_filter);
+}
+
+void collide_shape_vs_override_user_data(const JPH::Shape *p_shape1, const JPH::Shape *p_shape2, JPH::Vec3Arg p_scale1, JPH::Vec3Arg p_scale2, JPH::Mat44Arg p_center_of_mass_transform1, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, const JPH::CollideShapeSettings &p_collide_shape_settings, JPH::CollideShapeCollector &p_collector, const JPH::ShapeFilter &p_shape_filter) {
+ ERR_FAIL_COND(p_shape2->GetSubType() != JoltCustomShapeSubType::OVERRIDE_USER_DATA);
+
+ const JoltCustomUserDataShape *shape2 = static_cast(p_shape2);
+
+ JPH::CollisionDispatch::sCollideShapeVsShape(p_shape1, shape2->GetInnerShape(), p_scale1, p_scale2, p_center_of_mass_transform1, p_center_of_mass_transform2, p_sub_shape_id_creator1, p_sub_shape_id_creator2, p_collide_shape_settings, p_collector, p_shape_filter);
+}
+
+void cast_override_user_data_vs_shape(const JPH::ShapeCast &p_shape_cast, const JPH::ShapeCastSettings &p_shape_cast_settings, const JPH::Shape *p_shape, JPH::Vec3Arg p_scale, const JPH::ShapeFilter &p_shape_filter, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, JPH::CastShapeCollector &p_collector) {
+ ERR_FAIL_COND(p_shape_cast.mShape->GetSubType() != JoltCustomShapeSubType::OVERRIDE_USER_DATA);
+
+ const JoltCustomUserDataShape *shape = static_cast(p_shape_cast.mShape);
+ const JPH::ShapeCast shape_cast(shape->GetInnerShape(), p_shape_cast.mScale, p_shape_cast.mCenterOfMassStart, p_shape_cast.mDirection);
+
+ JPH::CollisionDispatch::sCastShapeVsShapeLocalSpace(shape_cast, p_shape_cast_settings, p_shape, p_scale, p_shape_filter, p_center_of_mass_transform2, p_sub_shape_id_creator1, p_sub_shape_id_creator2, p_collector);
+}
+
+void cast_shape_vs_override_user_data(const JPH::ShapeCast &p_shape_cast, const JPH::ShapeCastSettings &p_shape_cast_settings, const JPH::Shape *p_shape, JPH::Vec3Arg p_scale, const JPH::ShapeFilter &p_shape_filter, JPH::Mat44Arg p_center_of_mass_transform2, const JPH::SubShapeIDCreator &p_sub_shape_id_creator1, const JPH::SubShapeIDCreator &p_sub_shape_id_creator2, JPH::CastShapeCollector &p_collector) {
+ ERR_FAIL_COND(p_shape->GetSubType() != JoltCustomShapeSubType::OVERRIDE_USER_DATA);
+
+ const JoltCustomUserDataShape *shape = static_cast(p_shape);
+
+ JPH::CollisionDispatch::sCastShapeVsShapeLocalSpace(p_shape_cast, p_shape_cast_settings, shape->GetInnerShape(), p_scale, p_shape_filter, p_center_of_mass_transform2, p_sub_shape_id_creator1, p_sub_shape_id_creator2, p_collector);
+}
+
+} // namespace
+
+JPH::ShapeSettings::ShapeResult JoltCustomUserDataShapeSettings::Create() const {
+ if (mCachedResult.IsEmpty()) {
+ new JoltCustomUserDataShape(*this, mCachedResult);
+ }
+
+ return mCachedResult;
+}
+
+void JoltCustomUserDataShape::register_type() {
+ JPH::ShapeFunctions &shape_functions = JPH::ShapeFunctions::sGet(JoltCustomShapeSubType::OVERRIDE_USER_DATA);
+
+ shape_functions.mConstruct = construct_override_user_data;
+ shape_functions.mColor = JPH::Color::sCyan;
+
+ for (const JPH::EShapeSubType sub_type : JPH::sAllSubShapeTypes) {
+ JPH::CollisionDispatch::sRegisterCollideShape(JoltCustomShapeSubType::OVERRIDE_USER_DATA, sub_type, collide_override_user_data_vs_shape);
+ JPH::CollisionDispatch::sRegisterCollideShape(sub_type, JoltCustomShapeSubType::OVERRIDE_USER_DATA, collide_shape_vs_override_user_data);
+ JPH::CollisionDispatch::sRegisterCastShape(JoltCustomShapeSubType::OVERRIDE_USER_DATA, sub_type, cast_override_user_data_vs_shape);
+ JPH::CollisionDispatch::sRegisterCastShape(sub_type, JoltCustomShapeSubType::OVERRIDE_USER_DATA, cast_shape_vs_override_user_data);
+ }
+}
diff --git a/modules/jolt_physics/shapes/jolt_custom_user_data_shape.h b/modules/jolt_physics/shapes/jolt_custom_user_data_shape.h
new file mode 100644
index 000000000000..a6610c74625c
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_custom_user_data_shape.h
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* jolt_custom_user_data_shape.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CUSTOM_USER_DATA_SHAPE_H
+#define JOLT_CUSTOM_USER_DATA_SHAPE_H
+
+#include "jolt_custom_decorated_shape.h"
+#include "jolt_custom_shape_type.h"
+
+class JoltCustomUserDataShapeSettings final : public JoltCustomDecoratedShapeSettings {
+public:
+ using JoltCustomDecoratedShapeSettings::JoltCustomDecoratedShapeSettings;
+
+ virtual ShapeResult Create() const override;
+};
+
+class JoltCustomUserDataShape final : public JoltCustomDecoratedShape {
+public:
+ static void register_type();
+
+ JoltCustomUserDataShape() :
+ JoltCustomDecoratedShape(JoltCustomShapeSubType::OVERRIDE_USER_DATA) {}
+
+ JoltCustomUserDataShape(const JoltCustomUserDataShapeSettings &p_settings, ShapeResult &p_result) :
+ JoltCustomDecoratedShape(JoltCustomShapeSubType::OVERRIDE_USER_DATA, p_settings, p_result) {
+ if (!p_result.HasError()) {
+ p_result.Set(this);
+ }
+ }
+
+ virtual JPH::uint64 GetSubShapeUserData(const JPH::SubShapeID &p_sub_shape_id) const override { return GetUserData(); }
+};
+
+#endif // JOLT_CUSTOM_USER_DATA_SHAPE_H
diff --git a/modules/jolt_physics/shapes/jolt_cylinder_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_cylinder_shape_3d.cpp
new file mode 100644
index 000000000000..cafc360c78e3
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_cylinder_shape_3d.cpp
@@ -0,0 +1,100 @@
+/**************************************************************************/
+/* jolt_cylinder_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_cylinder_shape_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/CylinderShape.h"
+
+JPH::ShapeRefC JoltCylinderShape3D::_build() const {
+ const float half_height = height / 2.0f;
+ const float min_half_extent = MIN(half_height, radius);
+ const float actual_margin = MIN(margin, min_half_extent * JoltProjectSettings::get_collision_margin_fraction());
+
+ const JPH::CylinderShapeSettings shape_settings(half_height, radius, actual_margin);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics cylinder shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+Variant JoltCylinderShape3D::get_data() const {
+ Dictionary data;
+ data["height"] = height;
+ data["radius"] = radius;
+ return data;
+}
+
+void JoltCylinderShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
+
+ const Dictionary data = p_data;
+
+ const Variant maybe_height = data.get("height", Variant());
+ ERR_FAIL_COND(maybe_height.get_type() != Variant::FLOAT);
+
+ const Variant maybe_radius = data.get("radius", Variant());
+ ERR_FAIL_COND(maybe_radius.get_type() != Variant::FLOAT);
+
+ const float new_height = maybe_height;
+ const float new_radius = maybe_radius;
+
+ if (unlikely(new_height == height && new_radius == radius)) {
+ return;
+ }
+
+ height = new_height;
+ radius = new_radius;
+
+ destroy();
+}
+
+void JoltCylinderShape3D::set_margin(float p_margin) {
+ if (unlikely(margin == p_margin)) {
+ return;
+ }
+
+ margin = p_margin;
+
+ destroy();
+}
+
+AABB JoltCylinderShape3D::get_aabb() const {
+ const Vector3 half_extents(radius, height / 2.0f, radius);
+ return AABB(-half_extents, half_extents * 2.0f);
+}
+
+String JoltCylinderShape3D::to_string() const {
+ return vformat("{height=%f radius=%f margin=%f}", height, radius, margin);
+}
diff --git a/modules/jolt_physics/shapes/jolt_cylinder_shape_3d.h b/modules/jolt_physics/shapes/jolt_cylinder_shape_3d.h
new file mode 100644
index 000000000000..889a038c74a8
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_cylinder_shape_3d.h
@@ -0,0 +1,58 @@
+/**************************************************************************/
+/* jolt_cylinder_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CYLINDER_SHAPE_3D_H
+#define JOLT_CYLINDER_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltCylinderShape3D final : public JoltShape3D {
+ float height = 0.0f;
+ float radius = 0.0f;
+ float margin = 0.04f;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_CYLINDER; }
+ virtual bool is_convex() const override { return true; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return margin; }
+ virtual void set_margin(float p_margin) override;
+
+ virtual AABB get_aabb() const override;
+
+ String to_string() const;
+};
+
+#endif // JOLT_CYLINDER_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_height_map_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_height_map_shape_3d.cpp
new file mode 100644
index 000000000000..f4dd4e67d0d6
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_height_map_shape_3d.cpp
@@ -0,0 +1,235 @@
+/**************************************************************************/
+/* jolt_height_map_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_height_map_shape_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/HeightFieldShape.h"
+#include "Jolt/Physics/Collision/Shape/MeshShape.h"
+
+namespace {
+
+bool _is_vertex_hole(const JPH::VertexList &p_vertices, int p_index) {
+ const float height = p_vertices[(size_t)p_index].y;
+ return height == FLT_MAX || Math::is_nan(height);
+}
+
+bool _is_triangle_hole(const JPH::VertexList &p_vertices, int p_index0, int p_index1, int p_index2) {
+ return _is_vertex_hole(p_vertices, p_index0) || _is_vertex_hole(p_vertices, p_index1) || _is_vertex_hole(p_vertices, p_index2);
+}
+
+} // namespace
+
+JPH::ShapeRefC JoltHeightMapShape3D::_build() const {
+ const int height_count = (int)heights.size();
+ if (unlikely(height_count == 0)) {
+ return nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(height_count != width * depth, nullptr, vformat("Failed to build Jolt Physics height map shape with %s. Height count must be the product of width and depth. This shape belongs to %s.", to_string(), _owners_to_string()));
+ ERR_FAIL_COND_V_MSG(width < 2 || depth < 2, nullptr, vformat("Failed to build Jolt Physics height map shape with %s. The height map must be at least 2x2. This shape belongs to %s.", to_string(), _owners_to_string()));
+
+ if (width != depth) {
+ return JoltShape3D::with_double_sided(_build_mesh(), true);
+ }
+
+ const int block_size = 2; // Default of JPH::HeightFieldShapeSettings::mBlockSize
+ const int block_count = width / block_size;
+
+ if (block_count < 2) {
+ return JoltShape3D::with_double_sided(_build_mesh(), true);
+ }
+
+ return JoltShape3D::with_double_sided(_build_height_field(), true);
+}
+
+JPH::ShapeRefC JoltHeightMapShape3D::_build_height_field() const {
+ const int quad_count_x = width - 1;
+ const int quad_count_y = depth - 1;
+
+ const float offset_x = (float)-quad_count_x / 2.0f;
+ const float offset_y = (float)-quad_count_y / 2.0f;
+
+ // Jolt triangulates the height map differently from how Godot Physics does it, so we mirror the shape along the
+ // Z-axis to get the desired triangulation and reverse the rows to undo the mirroring.
+
+ LocalVector heights_rev;
+ heights_rev.resize(heights.size());
+
+ const real_t *heights_ptr = heights.ptr();
+ float *heights_rev_ptr = heights_rev.ptr();
+
+ for (int z = 0; z < depth; ++z) {
+ const int z_rev = (depth - 1) - z;
+
+ const real_t *row = heights_ptr + ptrdiff_t(z * width);
+ float *row_rev = heights_rev_ptr + ptrdiff_t(z_rev * width);
+
+ for (int x = 0; x < width; ++x) {
+ const real_t height = row[x];
+
+ // Godot has undocumented (accidental?) support for holes by passing NaN as the height value, whereas Jolt
+ // uses `FLT_MAX` instead, so we translate any NaN to `FLT_MAX` in order to be drop-in compatible.
+ row_rev[x] = Math::is_nan(height) ? FLT_MAX : (float)height;
+ }
+ }
+
+ JPH::HeightFieldShapeSettings shape_settings(heights_rev.ptr(), JPH::Vec3(offset_x, 0, offset_y), JPH::Vec3::sReplicate(1.0f), (JPH::uint32)width);
+
+ shape_settings.mBitsPerSample = shape_settings.CalculateBitsPerSampleForError(0.0f);
+ shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::get_active_edge_threshold();
+
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics height map shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return with_scale(shape_result.Get(), Vector3(1, 1, -1));
+}
+
+JPH::ShapeRefC JoltHeightMapShape3D::_build_mesh() const {
+ const int height_count = (int)heights.size();
+
+ const int quad_count_x = width - 1;
+ const int quad_count_z = depth - 1;
+
+ const int quad_count = quad_count_x * quad_count_z;
+ const int triangle_count = quad_count * 2;
+
+ JPH::VertexList vertices;
+ vertices.reserve((size_t)height_count);
+
+ JPH::IndexedTriangleList indices;
+ indices.reserve((size_t)triangle_count);
+
+ const float offset_x = (float)-quad_count_x / 2.0f;
+ const float offset_z = (float)-quad_count_z / 2.0f;
+
+ for (int z = 0; z < depth; ++z) {
+ for (int x = 0; x < width; ++x) {
+ const float vertex_x = offset_x + (float)x;
+ const float vertex_y = (float)heights[z * width + x];
+ const float vertex_z = offset_z + (float)z;
+
+ vertices.emplace_back(vertex_x, vertex_y, vertex_z);
+ }
+ }
+
+ for (int z = 0; z < quad_count_z; ++z) {
+ for (int x = 0; x < quad_count_x; ++x) {
+ const int index_lower_right = z * width + x;
+ const int index_lower_left = z * width + (x + 1);
+ const int index_upper_right = (z + 1) * width + x;
+ const int index_upper_left = (z + 1) * width + (x + 1);
+
+ if (!_is_triangle_hole(vertices, index_lower_right, index_upper_right, index_lower_left)) {
+ indices.emplace_back(index_lower_right, index_upper_right, index_lower_left);
+ }
+
+ if (!_is_triangle_hole(vertices, index_lower_left, index_upper_right, index_upper_left)) {
+ indices.emplace_back(index_lower_left, index_upper_right, index_upper_left);
+ }
+ }
+ }
+
+ JPH::MeshShapeSettings shape_settings(std::move(vertices), std::move(indices));
+ shape_settings.mActiveEdgeCosThresholdAngle = JoltProjectSettings::get_active_edge_threshold();
+
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics height map shape (as polygon) with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+AABB JoltHeightMapShape3D::_calculate_aabb() const {
+ AABB result;
+
+ const int quad_count_x = width - 1;
+ const int quad_count_z = depth - 1;
+
+ const float offset_x = (float)-quad_count_x / 2.0f;
+ const float offset_z = (float)-quad_count_z / 2.0f;
+
+ for (int z = 0; z < depth; ++z) {
+ for (int x = 0; x < width; ++x) {
+ const Vector3 vertex(offset_x + (float)x, (float)heights[z * width + x], offset_z + (float)z);
+
+ if (x == 0 && z == 0) {
+ result.position = vertex;
+ } else {
+ result.expand_to(vertex);
+ }
+ }
+ }
+
+ return result;
+}
+
+Variant JoltHeightMapShape3D::get_data() const {
+ Dictionary data;
+ data["width"] = width;
+ data["depth"] = depth;
+ data["heights"] = heights;
+ return data;
+}
+
+void JoltHeightMapShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
+
+ const Dictionary data = p_data;
+
+ const Variant maybe_heights = data.get("heights", Variant());
+
+#ifdef REAL_T_IS_DOUBLE
+ ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT64_ARRAY);
+#else
+ ERR_FAIL_COND(maybe_heights.get_type() != Variant::PACKED_FLOAT32_ARRAY);
+#endif
+
+ const Variant maybe_width = data.get("width", Variant());
+ ERR_FAIL_COND(maybe_width.get_type() != Variant::INT);
+
+ const Variant maybe_depth = data.get("depth", Variant());
+ ERR_FAIL_COND(maybe_depth.get_type() != Variant::INT);
+
+ heights = maybe_heights;
+ width = maybe_width;
+ depth = maybe_depth;
+
+ aabb = _calculate_aabb();
+
+ destroy();
+}
+
+String JoltHeightMapShape3D::to_string() const {
+ return vformat("{height_count=%d width=%d depth=%d}", heights.size(), width, depth);
+}
diff --git a/modules/jolt_physics/shapes/jolt_height_map_shape_3d.h b/modules/jolt_physics/shapes/jolt_height_map_shape_3d.h
new file mode 100644
index 000000000000..a929970f78f9
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_height_map_shape_3d.h
@@ -0,0 +1,69 @@
+/**************************************************************************/
+/* jolt_height_map_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_HEIGHT_MAP_SHAPE_3D_H
+#define JOLT_HEIGHT_MAP_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltHeightMapShape3D final : public JoltShape3D {
+ AABB aabb;
+
+#ifdef REAL_T_IS_DOUBLE
+ PackedFloat64Array heights;
+#else
+ PackedFloat32Array heights;
+#endif
+
+ int width = 0;
+ int depth = 0;
+
+ virtual JPH::ShapeRefC _build() const override;
+ JPH::ShapeRefC _build_height_field() const;
+ JPH::ShapeRefC _build_mesh() const;
+
+ AABB _calculate_aabb() const;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_HEIGHTMAP; }
+ virtual bool is_convex() const override { return false; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return 0.0f; }
+ virtual void set_margin(float p_margin) override {}
+
+ virtual AABB get_aabb() const override { return aabb; }
+
+ String to_string() const;
+};
+
+#endif // JOLT_HEIGHT_MAP_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_separation_ray_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_separation_ray_shape_3d.cpp
new file mode 100644
index 000000000000..ab7df972337d
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_separation_ray_shape_3d.cpp
@@ -0,0 +1,87 @@
+/**************************************************************************/
+/* jolt_separation_ray_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_separation_ray_shape_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "jolt_custom_ray_shape.h"
+
+#include "core/error/error_macros.h"
+
+JPH::ShapeRefC JoltSeparationRayShape3D::_build() const {
+ ERR_FAIL_COND_V_MSG(length <= 0.0f, nullptr, vformat("Failed to build Jolt Physics separation ray shape with %s. Its length must be greater than 0. This shape belongs to %s.", to_string(), _owners_to_string()));
+
+ const JoltCustomRayShapeSettings shape_settings(length, slide_on_slope);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics separation ray shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+Variant JoltSeparationRayShape3D::get_data() const {
+ Dictionary data;
+ data["length"] = length;
+ data["slide_on_slope"] = slide_on_slope;
+ return data;
+}
+
+void JoltSeparationRayShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);
+
+ const Dictionary data = p_data;
+
+ const Variant maybe_length = data.get("length", Variant());
+ ERR_FAIL_COND(maybe_length.get_type() != Variant::FLOAT);
+
+ const Variant maybe_slide_on_slope = data.get("slide_on_slope", Variant());
+ ERR_FAIL_COND(maybe_slide_on_slope.get_type() != Variant::BOOL);
+
+ const float new_length = maybe_length;
+ const bool new_slide_on_slope = maybe_slide_on_slope;
+
+ if (unlikely(new_length == length && new_slide_on_slope == slide_on_slope)) {
+ return;
+ }
+
+ length = new_length;
+ slide_on_slope = new_slide_on_slope;
+
+ destroy();
+}
+
+AABB JoltSeparationRayShape3D::get_aabb() const {
+ constexpr float size_xy = 0.1f;
+ constexpr float half_size_xy = size_xy / 2.0f;
+ return AABB(Vector3(-half_size_xy, -half_size_xy, 0.0f), Vector3(size_xy, size_xy, length));
+}
+
+String JoltSeparationRayShape3D::to_string() const {
+ return vformat("{length=%f slide_on_slope=%s}", length, slide_on_slope);
+}
diff --git a/modules/jolt_physics/shapes/jolt_separation_ray_shape_3d.h b/modules/jolt_physics/shapes/jolt_separation_ray_shape_3d.h
new file mode 100644
index 000000000000..b858d84febaa
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_separation_ray_shape_3d.h
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* jolt_separation_ray_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_SEPARATION_RAY_SHAPE_3D_H
+#define JOLT_SEPARATION_RAY_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltSeparationRayShape3D final : public JoltShape3D {
+ float length = 0.0f;
+ bool slide_on_slope = false;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_SEPARATION_RAY; }
+ virtual bool is_convex() const override { return true; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return 0.0f; }
+ virtual void set_margin(float p_margin) override {}
+
+ virtual AABB get_aabb() const override;
+
+ String to_string() const;
+};
+
+#endif // JOLT_SEPARATION_RAY_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_shape_3d.cpp
new file mode 100644
index 000000000000..dca898f6e415
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_shape_3d.cpp
@@ -0,0 +1,280 @@
+/**************************************************************************/
+/* jolt_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_shape_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_shaped_object_3d.h"
+#include "jolt_custom_double_sided_shape.h"
+#include "jolt_custom_user_data_shape.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/MutableCompoundShape.h"
+#include "Jolt/Physics/Collision/Shape/OffsetCenterOfMassShape.h"
+#include "Jolt/Physics/Collision/Shape/RotatedTranslatedShape.h"
+#include "Jolt/Physics/Collision/Shape/ScaledShape.h"
+#include "Jolt/Physics/Collision/Shape/SphereShape.h"
+#include "Jolt/Physics/Collision/Shape/StaticCompoundShape.h"
+
+namespace {
+
+constexpr float DEFAULT_SOLVER_BIAS = 0.0;
+
+} // namespace
+
+String JoltShape3D::_owners_to_string() const {
+ const int owner_count = ref_counts_by_owner.size();
+
+ if (owner_count == 0) {
+ return "'' and 0 other object(s)";
+ }
+
+ const JoltShapedObject3D &random_owner = *ref_counts_by_owner.begin()->key;
+
+ return vformat("'%s' and %d other object(s)", random_owner.to_string(), owner_count - 1);
+}
+
+JoltShape3D::~JoltShape3D() = default;
+
+void JoltShape3D::add_owner(JoltShapedObject3D *p_owner) {
+ ref_counts_by_owner[p_owner]++;
+}
+
+void JoltShape3D::remove_owner(JoltShapedObject3D *p_owner) {
+ if (--ref_counts_by_owner[p_owner] <= 0) {
+ ref_counts_by_owner.erase(p_owner);
+ }
+}
+
+void JoltShape3D::remove_self() {
+ // `remove_owner` will be called when we `remove_shape`, so we need to copy the map since the
+ // iterator would be invalidated from underneath us.
+ const HashMap ref_counts_by_owner_copy = ref_counts_by_owner;
+
+ for (const auto &[owner, ref_count] : ref_counts_by_owner_copy) {
+ owner->remove_shape(this);
+ }
+}
+
+float JoltShape3D::get_solver_bias() const {
+ return DEFAULT_SOLVER_BIAS;
+}
+
+void JoltShape3D::set_solver_bias(float p_bias) {
+ if (!Math::is_equal_approx(p_bias, DEFAULT_SOLVER_BIAS)) {
+ WARN_PRINT(vformat("Custom solver bias for shapes is not supported when using Jolt Physics. Any such value will be ignored. This shape belongs to %s.", _owners_to_string()));
+ }
+}
+
+JPH::ShapeRefC JoltShape3D::try_build() {
+ jolt_ref_mutex.lock();
+
+ if (jolt_ref == nullptr) {
+ jolt_ref = _build();
+ }
+
+ jolt_ref_mutex.unlock();
+
+ return jolt_ref;
+}
+
+void JoltShape3D::destroy() {
+ jolt_ref_mutex.lock();
+ jolt_ref = nullptr;
+ jolt_ref_mutex.unlock();
+
+ for (const auto &[owner, ref_count] : ref_counts_by_owner) {
+ owner->_shapes_changed();
+ }
+}
+
+JPH::ShapeRefC JoltShape3D::with_scale(const JPH::Shape *p_shape, const Vector3 &p_scale) {
+ ERR_FAIL_NULL_V(p_shape, nullptr);
+
+ const JPH::ScaledShapeSettings shape_settings(p_shape, to_jolt(p_scale));
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to scale shape with {scale=%v}. It returned the following error: '%s'.", p_scale, to_godot(shape_result.GetError())));
+
+ return shape_result.Get();
+}
+
+JPH::ShapeRefC JoltShape3D::with_basis_origin(const JPH::Shape *p_shape, const Basis &p_basis, const Vector3 &p_origin) {
+ ERR_FAIL_NULL_V(p_shape, nullptr);
+
+ const JPH::RotatedTranslatedShapeSettings shape_settings(to_jolt(p_origin), to_jolt(p_basis), p_shape);
+
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to offset shape with {basis=%s origin=%v}. It returned the following error: '%s'.", p_basis, p_origin, to_godot(shape_result.GetError())));
+
+ return shape_result.Get();
+}
+
+JPH::ShapeRefC JoltShape3D::with_center_of_mass_offset(const JPH::Shape *p_shape, const Vector3 &p_offset) {
+ ERR_FAIL_NULL_V(p_shape, nullptr);
+
+ const JPH::OffsetCenterOfMassShapeSettings shape_settings(to_jolt(p_offset), p_shape);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to offset center of mass with {offset=%v}. It returned the following error: '%s'.", p_offset, to_godot(shape_result.GetError())));
+
+ return shape_result.Get();
+}
+
+JPH::ShapeRefC JoltShape3D::with_center_of_mass(const JPH::Shape *p_shape, const Vector3 &p_center_of_mass) {
+ ERR_FAIL_NULL_V(p_shape, nullptr);
+
+ const Vector3 center_of_mass_inner = to_godot(p_shape->GetCenterOfMass());
+ const Vector3 center_of_mass_offset = p_center_of_mass - center_of_mass_inner;
+
+ if (center_of_mass_offset == Vector3()) {
+ return p_shape;
+ }
+
+ return with_center_of_mass_offset(p_shape, center_of_mass_offset);
+}
+
+JPH::ShapeRefC JoltShape3D::with_user_data(const JPH::Shape *p_shape, uint64_t p_user_data) {
+ JoltCustomUserDataShapeSettings shape_settings(p_shape);
+ shape_settings.mUserData = (JPH::uint64)p_user_data;
+
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to override user data. It returned the following error: '%s'.", to_godot(shape_result.GetError())));
+
+ return shape_result.Get();
+}
+
+JPH::ShapeRefC JoltShape3D::with_double_sided(const JPH::Shape *p_shape, bool p_back_face_collision) {
+ ERR_FAIL_NULL_V(p_shape, nullptr);
+
+ const JoltCustomDoubleSidedShapeSettings shape_settings(p_shape, p_back_face_collision);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to make shape double-sided. It returned the following error: '%s'.", to_godot(shape_result.GetError())));
+
+ return shape_result.Get();
+}
+
+JPH::ShapeRefC JoltShape3D::without_custom_shapes(const JPH::Shape *p_shape) {
+ switch (p_shape->GetSubType()) {
+ case JoltCustomShapeSubType::RAY:
+ case JoltCustomShapeSubType::MOTION: {
+ // Replace unsupported shapes with a small sphere.
+ return new JPH::SphereShape(0.1f);
+ }
+
+ case JoltCustomShapeSubType::OVERRIDE_USER_DATA:
+ case JoltCustomShapeSubType::DOUBLE_SIDED: {
+ const JPH::DecoratedShape *shape = static_cast(p_shape);
+
+ // Replace unsupported decorator shapes with the inner shape.
+ return without_custom_shapes(shape->GetInnerShape());
+ }
+
+ case JPH::EShapeSubType::StaticCompound: {
+ const JPH::StaticCompoundShape *shape = static_cast(p_shape);
+
+ JPH::StaticCompoundShapeSettings settings;
+
+ for (const JPH::CompoundShape::SubShape &sub_shape : shape->GetSubShapes()) {
+ settings.AddShape(shape->GetCenterOfMass() + sub_shape.GetPositionCOM() - sub_shape.GetRotation() * sub_shape.mShape->GetCenterOfMass(), sub_shape.GetRotation(), without_custom_shapes(sub_shape.mShape));
+ }
+
+ const JPH::ShapeSettings::ShapeResult shape_result = settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to recreate static compound shape during filtering of custom shapes. It returned the following error: '%s'.", to_godot(shape_result.GetError())));
+
+ return shape_result.Get();
+ }
+
+ case JPH::EShapeSubType::MutableCompound: {
+ const JPH::MutableCompoundShape *shape = static_cast(p_shape);
+
+ JPH::MutableCompoundShapeSettings settings;
+
+ for (const JPH::MutableCompoundShape::SubShape &sub_shape : shape->GetSubShapes()) {
+ settings.AddShape(shape->GetCenterOfMass() + sub_shape.GetPositionCOM() - sub_shape.GetRotation() * sub_shape.mShape->GetCenterOfMass(), sub_shape.GetRotation(), without_custom_shapes(sub_shape.mShape));
+ }
+
+ const JPH::ShapeSettings::ShapeResult shape_result = settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to recreate mutable compound shape during filtering of custom shapes. It returned the following error: '%s'.", to_godot(shape_result.GetError())));
+
+ return shape_result.Get();
+ }
+
+ case JPH::EShapeSubType::RotatedTranslated: {
+ const JPH::RotatedTranslatedShape *shape = static_cast(p_shape);
+
+ const JPH::Shape *inner_shape = shape->GetInnerShape();
+ const JPH::ShapeRefC new_inner_shape = without_custom_shapes(inner_shape);
+
+ if (inner_shape == new_inner_shape) {
+ return p_shape;
+ }
+
+ return new JPH::RotatedTranslatedShape(shape->GetPosition(), shape->GetRotation(), new_inner_shape);
+ }
+
+ case JPH::EShapeSubType::Scaled: {
+ const JPH::ScaledShape *shape = static_cast(p_shape);
+
+ const JPH::Shape *inner_shape = shape->GetInnerShape();
+ const JPH::ShapeRefC new_inner_shape = without_custom_shapes(inner_shape);
+
+ if (inner_shape == new_inner_shape) {
+ return p_shape;
+ }
+
+ return new JPH::ScaledShape(new_inner_shape, shape->GetScale());
+ }
+
+ case JPH::EShapeSubType::OffsetCenterOfMass: {
+ const JPH::OffsetCenterOfMassShape *shape = static_cast(p_shape);
+
+ const JPH::Shape *inner_shape = shape->GetInnerShape();
+ const JPH::ShapeRefC new_inner_shape = without_custom_shapes(inner_shape);
+
+ if (inner_shape == new_inner_shape) {
+ return p_shape;
+ }
+
+ return new JPH::OffsetCenterOfMassShape(new_inner_shape, shape->GetOffset());
+ }
+
+ default: {
+ return p_shape;
+ }
+ }
+}
+
+Vector3 JoltShape3D::make_scale_valid(const JPH::Shape *p_shape, const Vector3 &p_scale) {
+ return to_godot(p_shape->MakeScaleValid(to_jolt(p_scale)));
+}
+
+bool JoltShape3D::is_scale_valid(const Vector3 &p_scale, const Vector3 &p_valid_scale, real_t p_tolerance) {
+ return Math::is_equal_approx(p_scale.x, p_valid_scale.x, p_tolerance) && Math::is_equal_approx(p_scale.y, p_valid_scale.y, p_tolerance) && Math::is_equal_approx(p_scale.z, p_valid_scale.z, p_tolerance);
+}
diff --git a/modules/jolt_physics/shapes/jolt_shape_3d.h b/modules/jolt_physics/shapes/jolt_shape_3d.h
new file mode 100644
index 000000000000..3ababc3ad13b
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_shape_3d.h
@@ -0,0 +1,141 @@
+/**************************************************************************/
+/* jolt_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_SHAPE_3D_H
+#define JOLT_SHAPE_3D_H
+
+#include "core/error/error_macros.h"
+#include "core/math/aabb.h"
+#include "core/os/mutex.h"
+#include "core/templates/hash_map.h"
+#include "core/variant/variant.h"
+#include "servers/physics_server_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/Shape/Shape.h"
+
+class JoltShapedObject3D;
+
+class JoltShape3D {
+protected:
+ HashMap ref_counts_by_owner;
+ Mutex jolt_ref_mutex;
+ RID rid;
+ JPH::ShapeRefC jolt_ref;
+
+ virtual JPH::ShapeRefC _build() const = 0;
+
+ String _owners_to_string() const;
+
+public:
+ typedef PhysicsServer3D::ShapeType ShapeType;
+
+ virtual ~JoltShape3D() = 0;
+
+ RID get_rid() const { return rid; }
+ void set_rid(const RID &p_rid) { rid = p_rid; }
+
+ void add_owner(JoltShapedObject3D *p_owner);
+ void remove_owner(JoltShapedObject3D *p_owner);
+ void remove_self();
+
+ virtual ShapeType get_type() const = 0;
+ virtual bool is_convex() const = 0;
+
+ virtual Variant get_data() const = 0;
+ virtual void set_data(const Variant &p_data) = 0;
+
+ virtual float get_margin() const = 0;
+ virtual void set_margin(float p_margin) = 0;
+
+ virtual AABB get_aabb() const = 0;
+
+ float get_solver_bias() const;
+ void set_solver_bias(float p_bias);
+
+ JPH::ShapeRefC try_build();
+
+ void destroy();
+
+ const JPH::Shape *get_jolt_ref() const { return jolt_ref; }
+
+ static JPH::ShapeRefC with_scale(const JPH::Shape *p_shape, const Vector3 &p_scale);
+ static JPH::ShapeRefC with_basis_origin(const JPH::Shape *p_shape, const Basis &p_basis, const Vector3 &p_origin);
+ static JPH::ShapeRefC with_center_of_mass_offset(const JPH::Shape *p_shape, const Vector3 &p_offset);
+ static JPH::ShapeRefC with_center_of_mass(const JPH::Shape *p_shape, const Vector3 &p_center_of_mass);
+ static JPH::ShapeRefC with_user_data(const JPH::Shape *p_shape, uint64_t p_user_data);
+ static JPH::ShapeRefC with_double_sided(const JPH::Shape *p_shape, bool p_back_face_collision);
+ static JPH::ShapeRefC without_custom_shapes(const JPH::Shape *p_shape);
+
+ static Vector3 make_scale_valid(const JPH::Shape *p_shape, const Vector3 &p_scale);
+ static bool is_scale_valid(const Vector3 &p_scale, const Vector3 &p_valid_scale, real_t p_tolerance = 0.01f);
+};
+
+#ifdef DEBUG_ENABLED
+
+#define JOLT_ENSURE_SCALE_NOT_ZERO(m_transform, m_msg) \
+ if (unlikely((m_transform).basis.determinant() == 0.0f)) { \
+ WARN_PRINT(vformat("%s " \
+ "The basis of the transform was singular, which is not supported by Jolt Physics. " \
+ "This is likely caused by one or more axes having a scale of zero. " \
+ "The basis (and thus its scale) will be treated as identity.", \
+ m_msg)); \
+ \
+ (m_transform).basis = Basis(); \
+ } else \
+ ((void)0)
+
+#define ERR_PRINT_INVALID_SCALE_MSG(m_scale, m_valid_scale, m_msg) \
+ if (unlikely(!JoltShape3D::is_scale_valid(m_scale, valid_scale))) { \
+ ERR_PRINT(vformat("%s " \
+ "A scale of %v is not supported by Jolt Physics for this shape/body. " \
+ "The scale will instead be treated as %v.", \
+ m_msg, m_scale, valid_scale)); \
+ } else \
+ ((void)0)
+
+#else
+
+#define JOLT_ENSURE_SCALE_NOT_ZERO(m_transform, m_msg)
+
+#define ERR_PRINT_INVALID_SCALE_MSG(m_scale, m_valid_scale, m_msg)
+
+#endif
+
+#define JOLT_ENSURE_SCALE_VALID(m_shape, m_scale, m_msg) \
+ if (true) { \
+ const Vector3 valid_scale = JoltShape3D::make_scale_valid(m_shape, m_scale); \
+ ERR_PRINT_INVALID_SCALE_MSG(m_scale, valid_scale, m_msg); \
+ (m_scale) = valid_scale; \
+ } else \
+ ((void)0)
+
+#endif // JOLT_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_shape_instance_3d.cpp b/modules/jolt_physics/shapes/jolt_shape_instance_3d.cpp
new file mode 100644
index 000000000000..e21b5e595bd0
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_shape_instance_3d.cpp
@@ -0,0 +1,108 @@
+/**************************************************************************/
+/* jolt_shape_instance_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_shape_instance_3d.h"
+
+#include "jolt_shape_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/DecoratedShape.h"
+
+JoltShapeInstance3D::ShapeReference::ShapeReference(JoltShapedObject3D *p_parent, JoltShape3D *p_shape) :
+ parent(p_parent),
+ shape(p_shape) {
+ if (shape != nullptr) {
+ shape->add_owner(parent);
+ }
+}
+
+JoltShapeInstance3D::ShapeReference::ShapeReference(const ShapeReference &p_other) :
+ parent(p_other.parent),
+ shape(p_other.shape) {
+ if (shape != nullptr) {
+ shape->add_owner(parent);
+ }
+}
+
+JoltShapeInstance3D::ShapeReference::~ShapeReference() {
+ if (shape != nullptr) {
+ shape->remove_owner(parent);
+ }
+}
+
+JoltShapeInstance3D::ShapeReference &JoltShapeInstance3D::ShapeReference::operator=(const ShapeReference &p_other) {
+ if (shape != nullptr) {
+ shape->remove_owner(parent);
+ }
+
+ parent = p_other.parent;
+ shape = p_other.shape;
+
+ if (shape != nullptr) {
+ shape->add_owner(parent);
+ }
+
+ return *this;
+}
+
+JoltShapeInstance3D::JoltShapeInstance3D(JoltShapedObject3D *p_parent, JoltShape3D *p_shape, const Transform3D &p_transform, const Vector3 &p_scale, bool p_disabled) :
+ transform(p_transform),
+ scale(p_scale),
+ shape(p_parent, p_shape),
+ disabled(p_disabled) {
+}
+
+AABB JoltShapeInstance3D::get_aabb() const {
+ return get_transform_scaled().xform(shape->get_aabb());
+}
+
+bool JoltShapeInstance3D::try_build() {
+ ERR_FAIL_COND_V(is_disabled(), false);
+
+ const JPH::ShapeRefC maybe_new_shape = shape->try_build();
+
+ if (maybe_new_shape == nullptr) {
+ jolt_ref = nullptr;
+ return false;
+ }
+
+ if (jolt_ref != nullptr) {
+ const JPH::DecoratedShape *outer_shape = static_cast(jolt_ref.GetPtr());
+
+ if (outer_shape->GetInnerShape() == maybe_new_shape) {
+ return true;
+ }
+ }
+
+ jolt_ref = JoltShape3D::with_user_data(maybe_new_shape, (uint64_t)id);
+
+ return true;
+}
diff --git a/modules/jolt_physics/shapes/jolt_shape_instance_3d.h b/modules/jolt_physics/shapes/jolt_shape_instance_3d.h
new file mode 100644
index 000000000000..0a24d06a904d
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_shape_instance_3d.h
@@ -0,0 +1,103 @@
+/**************************************************************************/
+/* jolt_shape_instance_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_SHAPE_INSTANCE_3D_H
+#define JOLT_SHAPE_INSTANCE_3D_H
+
+#include "core/math/transform_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/Shape/Shape.h"
+
+class JoltShapedObject3D;
+class JoltShape3D;
+
+class JoltShapeInstance3D {
+ // This RAII helper exists solely to avoid needing to maintain copy construction/assignment in the shape instance.
+ // Ideally this would be move-only instead, but Godot's containers don't support that at the moment.
+ struct ShapeReference {
+ JoltShapedObject3D *parent = nullptr;
+ JoltShape3D *shape = nullptr;
+
+ ShapeReference() = default;
+ ShapeReference(JoltShapedObject3D *p_parent, JoltShape3D *p_shape);
+ ShapeReference(const ShapeReference &p_other);
+ ShapeReference(ShapeReference &&p_other) = delete;
+ ~ShapeReference();
+
+ ShapeReference &operator=(const ShapeReference &p_other);
+ ShapeReference &operator=(ShapeReference &&p_other) = delete;
+
+ JoltShape3D *operator*() const { return shape; }
+ JoltShape3D *operator->() const { return shape; }
+ operator JoltShape3D *() const { return shape; }
+ };
+
+ inline static uint32_t next_id = 1;
+
+ Transform3D transform;
+ Vector3 scale;
+ ShapeReference shape;
+ JPH::ShapeRefC jolt_ref;
+ uint32_t id = next_id++;
+ bool disabled = false;
+
+public:
+ JoltShapeInstance3D() = default;
+ JoltShapeInstance3D(JoltShapedObject3D *p_parent, JoltShape3D *p_shape, const Transform3D &p_transform = Transform3D(), const Vector3 &p_scale = Vector3(1.0f, 1.0f, 1.0f), bool p_disabled = false);
+
+ uint32_t get_id() const { return id; }
+
+ JoltShape3D *get_shape() const { return shape; }
+
+ const JPH::Shape *get_jolt_ref() const { return jolt_ref; }
+
+ const Transform3D &get_transform_unscaled() const { return transform; }
+ Transform3D get_transform_scaled() const { return transform.scaled_local(scale); }
+ void set_transform(const Transform3D &p_transform) { transform = p_transform; }
+
+ const Vector3 &get_scale() const { return scale; }
+ void set_scale(const Vector3 &p_scale) { scale = p_scale; }
+
+ AABB get_aabb() const;
+
+ bool is_built() const { return jolt_ref != nullptr; }
+
+ bool is_enabled() const { return !disabled; }
+ bool is_disabled() const { return disabled; }
+
+ void enable() { disabled = false; }
+ void disable() { disabled = true; }
+
+ bool try_build();
+};
+
+#endif // JOLT_SHAPE_INSTANCE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_sphere_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_sphere_shape_3d.cpp
new file mode 100644
index 000000000000..7157deb72d88
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_sphere_shape_3d.cpp
@@ -0,0 +1,73 @@
+/**************************************************************************/
+/* jolt_sphere_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_sphere_shape_3d.h"
+
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/SphereShape.h"
+
+JPH::ShapeRefC JoltSphereShape3D::_build() const {
+ ERR_FAIL_COND_V_MSG(radius <= 0.0f, nullptr, vformat("Failed to build Jolt Physics sphere shape with %s. Its radius must be greater than 0. This shape belongs to %s.", to_string(), _owners_to_string()));
+
+ const JPH::SphereShapeSettings shape_settings(radius);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics sphere shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+Variant JoltSphereShape3D::get_data() const {
+ return radius;
+}
+
+void JoltSphereShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::FLOAT);
+
+ const float new_radius = p_data;
+ if (unlikely(new_radius == radius)) {
+ return;
+ }
+
+ radius = new_radius;
+
+ destroy();
+}
+
+AABB JoltSphereShape3D::get_aabb() const {
+ const Vector3 half_extents(radius, radius, radius);
+ return AABB(-half_extents, half_extents * 2.0f);
+}
+
+String JoltSphereShape3D::to_string() const {
+ return vformat("{radius=%f}", radius);
+}
diff --git a/modules/jolt_physics/shapes/jolt_sphere_shape_3d.h b/modules/jolt_physics/shapes/jolt_sphere_shape_3d.h
new file mode 100644
index 000000000000..4127f6a5f989
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_sphere_shape_3d.h
@@ -0,0 +1,56 @@
+/**************************************************************************/
+/* jolt_sphere_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_SPHERE_SHAPE_3D_H
+#define JOLT_SPHERE_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltSphereShape3D final : public JoltShape3D {
+ float radius = 0.0f;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_SPHERE; }
+ virtual bool is_convex() const override { return true; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return 0.0f; }
+ virtual void set_margin(float p_margin) override {}
+
+ virtual AABB get_aabb() const override;
+
+ String to_string() const;
+};
+
+#endif // JOLT_SPHERE_SHAPE_3D_H
diff --git a/modules/jolt_physics/shapes/jolt_world_boundary_shape_3d.cpp b/modules/jolt_physics/shapes/jolt_world_boundary_shape_3d.cpp
new file mode 100644
index 000000000000..f930056f6ac3
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_world_boundary_shape_3d.cpp
@@ -0,0 +1,77 @@
+/**************************************************************************/
+/* jolt_world_boundary_shape_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_world_boundary_shape_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/Shape/PlaneShape.h"
+
+JPH::ShapeRefC JoltWorldBoundaryShape3D::_build() const {
+ const Plane normalized_plane = plane.normalized();
+ ERR_FAIL_COND_V_MSG(normalized_plane == Plane(), nullptr, vformat("Failed to build Jolt Physics world boundary shape with %s. The plane's normal must not be zero. This shape belongs to %s.", to_string(), _owners_to_string()));
+
+ const float half_size = JoltProjectSettings::get_world_boundary_shape_size() / 2.0f;
+ const JPH::PlaneShapeSettings shape_settings(to_jolt(normalized_plane), nullptr, half_size);
+ const JPH::ShapeSettings::ShapeResult shape_result = shape_settings.Create();
+ ERR_FAIL_COND_V_MSG(shape_result.HasError(), nullptr, vformat("Failed to build Jolt Physics world boundary shape with %s. It returned the following error: '%s'. This shape belongs to %s.", to_string(), to_godot(shape_result.GetError()), _owners_to_string()));
+
+ return shape_result.Get();
+}
+
+Variant JoltWorldBoundaryShape3D::get_data() const {
+ return plane;
+}
+
+void JoltWorldBoundaryShape3D::set_data(const Variant &p_data) {
+ ERR_FAIL_COND(p_data.get_type() != Variant::PLANE);
+
+ const Plane new_plane = p_data;
+ if (unlikely(new_plane == plane)) {
+ return;
+ }
+
+ plane = p_data;
+
+ destroy();
+}
+
+AABB JoltWorldBoundaryShape3D::get_aabb() const {
+ const float size = JoltProjectSettings::get_world_boundary_shape_size();
+ const float half_size = size / 2.0f;
+ return AABB(Vector3(-half_size, -half_size, -half_size), Vector3(size, half_size, size));
+}
+
+String JoltWorldBoundaryShape3D::to_string() const {
+ return vformat("{plane=%s}", plane);
+}
diff --git a/modules/jolt_physics/shapes/jolt_world_boundary_shape_3d.h b/modules/jolt_physics/shapes/jolt_world_boundary_shape_3d.h
new file mode 100644
index 000000000000..2d22c0f5f594
--- /dev/null
+++ b/modules/jolt_physics/shapes/jolt_world_boundary_shape_3d.h
@@ -0,0 +1,56 @@
+/**************************************************************************/
+/* jolt_world_boundary_shape_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_WORLD_BOUNDARY_SHAPE_3D_H
+#define JOLT_WORLD_BOUNDARY_SHAPE_3D_H
+
+#include "jolt_shape_3d.h"
+
+class JoltWorldBoundaryShape3D final : public JoltShape3D {
+ Plane plane;
+
+ virtual JPH::ShapeRefC _build() const override;
+
+public:
+ virtual ShapeType get_type() const override { return ShapeType::SHAPE_WORLD_BOUNDARY; }
+ virtual bool is_convex() const override { return false; }
+
+ virtual Variant get_data() const override;
+ virtual void set_data(const Variant &p_data) override;
+
+ virtual float get_margin() const override { return 0.0f; }
+ virtual void set_margin(float p_margin) override {}
+
+ virtual AABB get_aabb() const override;
+
+ String to_string() const;
+};
+
+#endif // JOLT_WORLD_BOUNDARY_SHAPE_3D_H
diff --git a/modules/jolt_physics/spaces/jolt_body_accessor_3d.cpp b/modules/jolt_physics/spaces/jolt_body_accessor_3d.cpp
new file mode 100644
index 000000000000..e2128a1f205e
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_body_accessor_3d.cpp
@@ -0,0 +1,198 @@
+/**************************************************************************/
+/* jolt_body_accessor_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_body_accessor_3d.h"
+
+#include "jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+namespace {
+
+template
+struct VariantVisitors : TTypes... {
+ using TTypes::operator()...;
+};
+
+template
+VariantVisitors(TTypes...) -> VariantVisitors;
+
+} // namespace
+
+JoltBodyAccessor3D::JoltBodyAccessor3D(const JoltSpace3D *p_space) :
+ space(p_space) {
+}
+
+JoltBodyAccessor3D::~JoltBodyAccessor3D() = default;
+
+void JoltBodyAccessor3D::acquire(const JPH::BodyID *p_ids, int p_id_count) {
+ ERR_FAIL_NULL(space);
+
+ lock_iface = &space->get_lock_iface();
+ ids = BodyIDSpan(p_ids, p_id_count);
+ _acquire_internal(p_ids, p_id_count);
+}
+
+void JoltBodyAccessor3D::acquire(const JPH::BodyID &p_id) {
+ ERR_FAIL_NULL(space);
+
+ lock_iface = &space->get_lock_iface();
+ ids = p_id;
+ _acquire_internal(&p_id, 1);
+}
+
+void JoltBodyAccessor3D::acquire_active() {
+ const JPH::PhysicsSystem &physics_system = space->get_physics_system();
+
+ acquire(physics_system.GetActiveBodiesUnsafe(JPH::EBodyType::RigidBody), (int)physics_system.GetNumActiveBodies(JPH::EBodyType::RigidBody));
+}
+
+void JoltBodyAccessor3D::acquire_all() {
+ ERR_FAIL_NULL(space);
+
+ lock_iface = &space->get_lock_iface();
+
+ JPH::BodyIDVector *vector = std::get_if(&ids);
+
+ if (vector == nullptr) {
+ ids = JPH::BodyIDVector();
+ vector = std::get_if(&ids);
+ }
+
+ space->get_physics_system().GetBodies(*vector);
+
+ _acquire_internal(vector->data(), (int)vector->size());
+}
+
+void JoltBodyAccessor3D::release() {
+ _release_internal();
+ lock_iface = nullptr;
+}
+
+const JPH::BodyID *JoltBodyAccessor3D::get_ids() const {
+ ERR_FAIL_COND_V(not_acquired(), nullptr);
+
+ return std::visit(
+ VariantVisitors{
+ [](const JPH::BodyID &p_id) { return &p_id; },
+ [](const JPH::BodyIDVector &p_vector) { return p_vector.data(); },
+ [](const BodyIDSpan &p_span) { return p_span.ptr; } },
+ ids);
+}
+
+int JoltBodyAccessor3D::get_count() const {
+ ERR_FAIL_COND_V(not_acquired(), 0);
+
+ return std::visit(
+ VariantVisitors{
+ [](const JPH::BodyID &p_id) { return 1; },
+ [](const JPH::BodyIDVector &p_vector) { return (int)p_vector.size(); },
+ [](const BodyIDSpan &p_span) { return p_span.count; } },
+ ids);
+}
+
+const JPH::BodyID &JoltBodyAccessor3D::get_at(int p_index) const {
+ CRASH_BAD_INDEX(p_index, get_count());
+ return get_ids()[p_index];
+}
+
+void JoltBodyReader3D::_acquire_internal(const JPH::BodyID *p_ids, int p_id_count) {
+ mutex_mask = lock_iface->GetMutexMask(p_ids, p_id_count);
+ lock_iface->LockRead(mutex_mask);
+}
+
+void JoltBodyReader3D::_release_internal() {
+ ERR_FAIL_COND(not_acquired());
+ lock_iface->UnlockRead(mutex_mask);
+}
+
+JoltBodyReader3D::JoltBodyReader3D(const JoltSpace3D *p_space) :
+ JoltBodyAccessor3D(p_space) {
+}
+
+const JPH::Body *JoltBodyReader3D::try_get(const JPH::BodyID &p_id) const {
+ if (unlikely(p_id.IsInvalid())) {
+ return nullptr;
+ }
+
+ ERR_FAIL_COND_V(not_acquired(), nullptr);
+
+ return lock_iface->TryGetBody(p_id);
+}
+
+const JPH::Body *JoltBodyReader3D::try_get(int p_index) const {
+ const int count = get_count();
+ if (unlikely(p_index < 0 || p_index >= count)) {
+ return nullptr;
+ }
+
+ return try_get(get_at(p_index));
+}
+
+const JPH::Body *JoltBodyReader3D::try_get() const {
+ return try_get(0);
+}
+
+void JoltBodyWriter3D::_acquire_internal(const JPH::BodyID *p_ids, int p_id_count) {
+ mutex_mask = lock_iface->GetMutexMask(p_ids, p_id_count);
+ lock_iface->LockWrite(mutex_mask);
+}
+
+void JoltBodyWriter3D::_release_internal() {
+ ERR_FAIL_COND(not_acquired());
+ lock_iface->UnlockWrite(mutex_mask);
+}
+
+JoltBodyWriter3D::JoltBodyWriter3D(const JoltSpace3D *p_space) :
+ JoltBodyAccessor3D(p_space) {
+}
+
+JPH::Body *JoltBodyWriter3D::try_get(const JPH::BodyID &p_id) const {
+ if (unlikely(p_id.IsInvalid())) {
+ return nullptr;
+ }
+
+ ERR_FAIL_COND_V(not_acquired(), nullptr);
+
+ return lock_iface->TryGetBody(p_id);
+}
+
+JPH::Body *JoltBodyWriter3D::try_get(int p_index) const {
+ const int count = get_count();
+ if (unlikely(p_index < 0 || p_index >= count)) {
+ return nullptr;
+ }
+
+ return try_get(get_at(p_index));
+}
+
+JPH::Body *JoltBodyWriter3D::try_get() const {
+ return try_get(0);
+}
diff --git a/modules/jolt_physics/spaces/jolt_body_accessor_3d.h b/modules/jolt_physics/spaces/jolt_body_accessor_3d.h
new file mode 100644
index 000000000000..0d2681468368
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_body_accessor_3d.h
@@ -0,0 +1,215 @@
+/**************************************************************************/
+/* jolt_body_accessor_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_BODY_ACCESSOR_3D_H
+#define JOLT_BODY_ACCESSOR_3D_H
+
+#include "../objects/jolt_object_3d.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Body/BodyLockInterface.h"
+
+#include
+
+class JoltArea3D;
+class JoltBody3D;
+class JoltShapedObject3D;
+class JoltSpace3D;
+
+class JoltBodyAccessor3D {
+protected:
+ struct BodyIDSpan {
+ BodyIDSpan(const JPH::BodyID *p_ptr, int p_count) :
+ ptr(p_ptr), count(p_count) {}
+
+ const JPH::BodyID *ptr;
+ int count;
+ };
+
+ virtual void _acquire_internal(const JPH::BodyID *p_ids, int p_id_count) = 0;
+ virtual void _release_internal() = 0;
+
+ const JoltSpace3D *space = nullptr;
+
+ const JPH::BodyLockInterface *lock_iface = nullptr;
+
+ std::variant ids;
+
+public:
+ explicit JoltBodyAccessor3D(const JoltSpace3D *p_space);
+ virtual ~JoltBodyAccessor3D() = 0;
+
+ void acquire(const JPH::BodyID *p_ids, int p_id_count);
+ void acquire(const JPH::BodyID &p_id);
+ void acquire_active();
+ void acquire_all();
+ void release();
+
+ bool is_acquired() const { return lock_iface != nullptr; }
+ bool not_acquired() const { return lock_iface == nullptr; }
+
+ const JoltSpace3D &get_space() const { return *space; }
+ const JPH::BodyID *get_ids() const;
+ int get_count() const;
+
+ const JPH::BodyID &get_at(int p_index) const;
+};
+
+class JoltBodyReader3D final : public JoltBodyAccessor3D {
+ virtual void _acquire_internal(const JPH::BodyID *p_ids, int p_id_count) override;
+ virtual void _release_internal() override;
+
+ JPH::BodyLockInterface::MutexMask mutex_mask = 0;
+
+public:
+ explicit JoltBodyReader3D(const JoltSpace3D *p_space);
+
+ const JPH::Body *try_get(const JPH::BodyID &p_id) const;
+ const JPH::Body *try_get(int p_index) const;
+ const JPH::Body *try_get() const;
+};
+
+class JoltBodyWriter3D final : public JoltBodyAccessor3D {
+ virtual void _acquire_internal(const JPH::BodyID *p_ids, int p_id_count) override;
+ virtual void _release_internal() override;
+
+ JPH::BodyLockInterface::MutexMask mutex_mask = 0;
+
+public:
+ explicit JoltBodyWriter3D(const JoltSpace3D *p_space);
+
+ JPH::Body *try_get(const JPH::BodyID &p_id) const;
+ JPH::Body *try_get(int p_index) const;
+ JPH::Body *try_get() const;
+};
+
+template
+class JoltScopedBodyAccessor3D {
+ TBodyAccessor inner;
+
+public:
+ JoltScopedBodyAccessor3D(const JoltSpace3D &p_space, const JPH::BodyID *p_ids, int p_id_count) :
+ inner(&p_space) { inner.acquire(p_ids, p_id_count); }
+
+ JoltScopedBodyAccessor3D(const JoltSpace3D &p_space, const JPH::BodyID &p_id) :
+ inner(&p_space) { inner.acquire(p_id); }
+
+ JoltScopedBodyAccessor3D(const JoltScopedBodyAccessor3D &p_other) = delete;
+ JoltScopedBodyAccessor3D(JoltScopedBodyAccessor3D &&p_other) = default;
+ ~JoltScopedBodyAccessor3D() { inner.release(); }
+
+ const JoltSpace3D &get_space() const { return inner.get_space(); }
+ int get_count() const { return inner.get_count(); }
+ const JPH::BodyID &get_at(int p_index) const { return inner.get_at(p_index); }
+
+ JoltScopedBodyAccessor3D &operator=(const JoltScopedBodyAccessor3D &p_other) = delete;
+ JoltScopedBodyAccessor3D &operator=(JoltScopedBodyAccessor3D &&p_other) = default;
+
+ decltype(auto) try_get(const JPH::BodyID &p_id) const { return inner.try_get(p_id); }
+ decltype(auto) try_get(int p_index) const { return inner.try_get(p_index); }
+ decltype(auto) try_get() const { return inner.try_get(); }
+};
+
+template
+class JoltAccessibleBody3D {
+ TAccessor accessor;
+ TBody *body = nullptr;
+
+public:
+ JoltAccessibleBody3D(const JoltSpace3D &p_space, const JPH::BodyID &p_id) :
+ accessor(p_space, p_id), body(accessor.try_get()) {}
+
+ bool is_valid() const { return body != nullptr; }
+ bool is_invalid() const { return body == nullptr; }
+
+ JoltObject3D *as_object() const {
+ if (body != nullptr) {
+ return reinterpret_cast(body->GetUserData());
+ } else {
+ return nullptr;
+ }
+ }
+
+ JoltShapedObject3D *as_shaped() const {
+ if (JoltObject3D *object = as_object(); object != nullptr && object->is_shaped()) {
+ return reinterpret_cast(body->GetUserData());
+ } else {
+ return nullptr;
+ }
+ }
+
+ JoltBody3D *as_body() const {
+ if (JoltObject3D *object = as_object(); object != nullptr && object->is_body()) {
+ return reinterpret_cast(body->GetUserData());
+ } else {
+ return nullptr;
+ }
+ }
+
+ JoltArea3D *as_area() const {
+ if (JoltObject3D *object = as_object(); object != nullptr && object->is_area()) {
+ return reinterpret_cast(body->GetUserData());
+ } else {
+ return nullptr;
+ }
+ }
+
+ TBody *operator->() const { return body; }
+ TBody &operator*() const { return *body; }
+
+ explicit operator TBody *() const { return body; }
+};
+
+template
+class JoltAccessibleBodies3D {
+ TAccessor accessor;
+
+public:
+ JoltAccessibleBodies3D(const JoltSpace3D &p_space, const JPH::BodyID *p_ids, int p_id_count) :
+ accessor(p_space, p_ids, p_id_count) {}
+
+ JoltAccessibleBody3D operator[](int p_index) const {
+ const JPH::BodyID &body_id = p_index < accessor.get_count() ? accessor.get_at(p_index) : JPH::BodyID();
+
+ return { accessor.get_space(), body_id };
+ }
+};
+
+typedef JoltScopedBodyAccessor3D JoltScopedBodyReader3D;
+typedef JoltScopedBodyAccessor3D JoltScopedBodyWriter3D;
+
+typedef JoltAccessibleBody3D JoltReadableBody3D;
+typedef JoltAccessibleBody3D JoltWritableBody3D;
+
+typedef JoltAccessibleBodies3D JoltReadableBodies3D;
+typedef JoltAccessibleBodies3D JoltWritableBodies3D;
+
+#endif // JOLT_BODY_ACCESSOR_3D_H
diff --git a/modules/jolt_physics/spaces/jolt_broad_phase_layer.h b/modules/jolt_physics/spaces/jolt_broad_phase_layer.h
new file mode 100644
index 000000000000..8cc115e1d42a
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_broad_phase_layer.h
@@ -0,0 +1,52 @@
+/**************************************************************************/
+/* jolt_broad_phase_layer.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_BROAD_PHASE_LAYER_H
+#define JOLT_BROAD_PHASE_LAYER_H
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
+
+#include
+
+namespace JoltBroadPhaseLayer {
+
+constexpr JPH::BroadPhaseLayer BODY_STATIC(0);
+constexpr JPH::BroadPhaseLayer BODY_STATIC_BIG(1);
+constexpr JPH::BroadPhaseLayer BODY_DYNAMIC(2);
+constexpr JPH::BroadPhaseLayer AREA_DETECTABLE(3);
+constexpr JPH::BroadPhaseLayer AREA_UNDETECTABLE(4);
+
+constexpr uint32_t COUNT = 5;
+
+} // namespace JoltBroadPhaseLayer
+
+#endif // JOLT_BROAD_PHASE_LAYER_H
diff --git a/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp b/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp
new file mode 100644
index 000000000000..2442a8f7af39
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_contact_listener_3d.cpp
@@ -0,0 +1,547 @@
+/**************************************************************************/
+/* jolt_contact_listener_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_contact_listener_3d.h"
+
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_area_3d.h"
+#include "../objects/jolt_body_3d.h"
+#include "../objects/jolt_soft_body_3d.h"
+#include "jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Physics/Collision/EstimateCollisionResponse.h"
+#include "Jolt/Physics/SoftBody/SoftBodyManifold.h"
+
+void JoltContactListener3D::OnContactAdded(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
+ _try_override_collision_response(p_body1, p_body2, p_settings);
+ _try_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);
+
+#ifdef DEBUG_ENABLED
+ _try_add_debug_contacts(p_body1, p_body2, p_manifold);
+#endif
+}
+
+void JoltContactListener3D::OnContactPersisted(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
+ _try_override_collision_response(p_body1, p_body2, p_settings);
+ _try_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);
+
+#ifdef DEBUG_ENABLED
+ _try_add_debug_contacts(p_body1, p_body2, p_manifold);
+#endif
+}
+
+void JoltContactListener3D::OnContactRemoved(const JPH::SubShapeIDPair &p_shape_pair) {
+ if (_try_remove_contacts(p_shape_pair)) {
+ return;
+ }
+
+ if (_try_remove_area_overlap(p_shape_pair)) {
+ return;
+ }
+}
+
+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);
+}
+
+#endif
+
+bool JoltContactListener3D::_is_listening_for(const JPH::Body &p_body) const {
+ return listening_for.has(p_body.GetID());
+}
+
+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()) {
+ return false;
+ }
+
+ if (!p_jolt_body1.IsDynamic() && !p_jolt_body2.IsDynamic()) {
+ return false;
+ }
+
+ const JoltBody3D *body1 = reinterpret_cast(p_jolt_body1.GetUserData());
+ const JoltBody3D *body2 = reinterpret_cast(p_jolt_body2.GetUserData());
+
+ const bool can_collide1 = body1->can_collide_with(*body2);
+ const bool can_collide2 = body2->can_collide_with(*body1);
+
+ if (can_collide1 && !can_collide2) {
+ p_settings.mInvMassScale2 = 0.0f;
+ p_settings.mInvInertiaScale2 = 0.0f;
+ } else if (can_collide2 && !can_collide1) {
+ p_settings.mInvMassScale1 = 0.0f;
+ p_settings.mInvInertiaScale1 = 0.0f;
+ }
+
+ return true;
+}
+
+bool JoltContactListener3D::_try_override_collision_response(const JPH::Body &p_jolt_soft_body, const JPH::Body &p_jolt_other_body, JPH::SoftBodyContactSettings &p_settings) {
+ if (p_jolt_other_body.IsSensor()) {
+ return false;
+ }
+
+ const JoltSoftBody3D *soft_body = reinterpret_cast(p_jolt_soft_body.GetUserData());
+ const JoltBody3D *other_body = reinterpret_cast(p_jolt_other_body.GetUserData());
+
+ const bool can_collide1 = soft_body->can_collide_with(*other_body);
+ const bool can_collide2 = other_body->can_collide_with(*soft_body);
+
+ if (can_collide1 && !can_collide2) {
+ p_settings.mInvMassScale2 = 0.0f;
+ p_settings.mInvInertiaScale2 = 0.0f;
+ } else if (can_collide2 && !can_collide1) {
+ p_settings.mInvMassScale1 = 0.0f;
+ }
+
+ return true;
+}
+
+bool JoltContactListener3D::_try_apply_surface_velocities(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()) {
+ return false;
+ }
+
+ const bool supports_surface_velocity1 = !p_jolt_body1.IsDynamic();
+ const bool supports_surface_velocity2 = !p_jolt_body2.IsDynamic();
+
+ if (supports_surface_velocity1 == supports_surface_velocity2) {
+ return false;
+ }
+
+ const JoltBody3D *body1 = reinterpret_cast(p_jolt_body1.GetUserData());
+ const JoltBody3D *body2 = reinterpret_cast(p_jolt_body2.GetUserData());
+
+ const bool has_surface_velocity1 = supports_surface_velocity1 && (body1->get_linear_surface_velocity() != Vector3() || body1->get_angular_surface_velocity() != Vector3());
+ const bool has_surface_velocity2 = supports_surface_velocity2 && (body2->get_linear_surface_velocity() != Vector3() || body2->get_angular_surface_velocity() != Vector3());
+
+ if (has_surface_velocity1 == has_surface_velocity2) {
+ return false;
+ }
+
+ const JPH::Vec3 linear_velocity1 = to_jolt(body1->get_linear_surface_velocity());
+ const JPH::Vec3 angular_velocity1 = to_jolt(body1->get_angular_surface_velocity());
+
+ const JPH::Vec3 linear_velocity2 = to_jolt(body2->get_linear_surface_velocity());
+ const JPH::Vec3 angular_velocity2 = to_jolt(body2->get_angular_surface_velocity());
+
+ const JPH::RVec3 com1 = p_jolt_body1.GetCenterOfMassPosition();
+ const JPH::RVec3 com2 = p_jolt_body2.GetCenterOfMassPosition();
+ const JPH::Vec3 rel_com2 = JPH::Vec3(com2 - com1);
+
+ const JPH::Vec3 angular_linear_velocity2 = rel_com2.Cross(angular_velocity2);
+ const JPH::Vec3 total_linear_velocity2 = linear_velocity2 + angular_linear_velocity2;
+
+ p_settings.mRelativeLinearSurfaceVelocity = total_linear_velocity2 - linear_velocity1;
+ p_settings.mRelativeAngularSurfaceVelocity = angular_velocity2 - angular_velocity1;
+
+ return true;
+}
+
+bool JoltContactListener3D::_try_add_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) {
+ if (p_body1.IsSensor() || p_body2.IsSensor()) {
+ return false;
+ }
+
+ if (!_is_listening_for(p_body1) && !_is_listening_for(p_body2)) {
+ return false;
+ }
+
+ const JPH::SubShapeIDPair shape_pair(p_body1.GetID(), p_manifold.mSubShapeID1, p_body2.GetID(), p_manifold.mSubShapeID2);
+
+ Manifold &manifold = [&]() -> Manifold & {
+ const MutexLock write_lock(write_mutex);
+ return manifolds_by_shape_pair[shape_pair];
+ }();
+
+ const JPH::uint contact_count = p_manifold.mRelativeContactPointsOn1.size();
+
+ manifold.contacts1.reserve((uint32_t)contact_count);
+ manifold.contacts2.reserve((uint32_t)contact_count);
+ manifold.depth = p_manifold.mPenetrationDepth;
+
+ JPH::CollisionEstimationResult collision;
+
+ JPH::EstimateCollisionResponse(p_body1, p_body2, p_manifold, collision, p_settings.mCombinedFriction, p_settings.mCombinedRestitution, JoltProjectSettings::get_bounce_velocity_threshold(), 5);
+
+ for (JPH::uint i = 0; i < contact_count; ++i) {
+ const JPH::RVec3 relative_point1 = JPH::RVec3(p_manifold.mRelativeContactPointsOn1[i]);
+ const JPH::RVec3 relative_point2 = JPH::RVec3(p_manifold.mRelativeContactPointsOn2[i]);
+
+ const JPH::RVec3 world_point1 = p_manifold.mBaseOffset + relative_point1;
+ const JPH::RVec3 world_point2 = p_manifold.mBaseOffset + relative_point2;
+
+ const JPH::Vec3 velocity1 = p_body1.GetPointVelocity(world_point1);
+ const JPH::Vec3 velocity2 = p_body2.GetPointVelocity(world_point2);
+
+ const JPH::CollisionEstimationResult::Impulse &impulse = collision.mImpulses[i];
+
+ const JPH::Vec3 contact_impulse = p_manifold.mWorldSpaceNormal * impulse.mContactImpulse;
+ const JPH::Vec3 friction_impulse1 = collision.mTangent1 * impulse.mFrictionImpulse1;
+ const JPH::Vec3 friction_impulse2 = collision.mTangent2 * impulse.mFrictionImpulse2;
+ const JPH::Vec3 combined_impulse = contact_impulse + friction_impulse1 + friction_impulse2;
+
+ Contact contact1;
+ contact1.point_self = world_point1;
+ contact1.point_other = world_point2;
+ contact1.normal = -p_manifold.mWorldSpaceNormal;
+ contact1.velocity_self = velocity1;
+ contact1.velocity_other = velocity2;
+ contact1.impulse = -combined_impulse;
+ manifold.contacts1.push_back(contact1);
+
+ Contact contact2;
+ contact2.point_self = world_point2;
+ contact2.point_other = world_point1;
+ contact2.normal = p_manifold.mWorldSpaceNormal;
+ contact2.velocity_self = velocity2;
+ contact2.velocity_other = velocity1;
+ contact2.impulse = combined_impulse;
+ manifold.contacts2.push_back(contact2);
+ }
+
+ return true;
+}
+
+bool JoltContactListener3D::_try_evaluate_area_overlap(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold) {
+ if (!p_body1.IsSensor() && !p_body2.IsSensor()) {
+ return false;
+ }
+
+ auto evaluate = [&](const auto &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 (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 JoltObject3D *object1 = reinterpret_cast(p_body1.GetUserData());
+ const JoltObject3D *object2 = reinterpret_cast(p_body2.GetUserData());
+
+ const JoltArea3D *area1 = object1->as_area();
+ const JoltArea3D *area2 = object2->as_area();
+
+ const JoltBody3D *body1 = object1->as_body();
+ const JoltBody3D *body2 = object2->as_body();
+
+ if (area1 != nullptr && area2 != nullptr) {
+ evaluate(*area1, *area2, shape_pair1);
+ evaluate(*area2, *area1, shape_pair2);
+ } else if (area1 != nullptr && body2 != nullptr) {
+ evaluate(*area1, *body2, shape_pair1);
+ } else if (area2 != nullptr && body1 != nullptr) {
+ evaluate(*area2, *body1, shape_pair2);
+ }
+
+ return true;
+}
+
+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);
+}
+
+bool JoltContactListener3D::_try_remove_area_overlap(const JPH::SubShapeIDPair &p_shape_pair) {
+ const JPH::SubShapeIDPair swapped_shape_pair(p_shape_pair.GetBody2ID(), p_shape_pair.GetSubShapeID2(), p_shape_pair.GetBody1ID(), p_shape_pair.GetSubShapeID1());
+
+ const MutexLock write_lock(write_mutex);
+
+ bool removed = false;
+
+ if (area_overlaps.erase(p_shape_pair)) {
+ area_exits.insert(p_shape_pair);
+ removed = true;
+ }
+
+ if (area_overlaps.erase(swapped_shape_pair)) {
+ area_exits.insert(swapped_shape_pair);
+ removed = true;
+ }
+
+ return removed;
+}
+
+#ifdef DEBUG_ENABLED
+
+bool JoltContactListener3D::_try_add_debug_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold) {
+ if (p_body1.IsSensor() || p_body2.IsSensor()) {
+ return false;
+ }
+
+ const int64_t max_count = debug_contacts.size();
+
+ if (max_count == 0) {
+ return false;
+ }
+
+ const int additional_pairs = (int)p_manifold.mRelativeContactPointsOn1.size();
+ const int additional_contacts = additional_pairs * 2;
+
+ int current_count = debug_contact_count.load(std::memory_order_relaxed);
+ bool exchanged = false;
+
+ do {
+ const int new_count = current_count + additional_contacts;
+
+ if (new_count > max_count) {
+ return false;
+ }
+
+ exchanged = debug_contact_count.compare_exchange_weak(current_count, new_count, std::memory_order_release, std::memory_order_relaxed);
+ } while (!exchanged);
+
+ for (int i = 0; i < additional_pairs; ++i) {
+ const int pair_index = current_count + i * 2;
+
+ const JPH::RVec3 point_on_1 = p_manifold.GetWorldSpaceContactPointOn1((JPH::uint)i);
+ const JPH::RVec3 point_on_2 = p_manifold.GetWorldSpaceContactPointOn2((JPH::uint)i);
+
+ debug_contacts.write[pair_index + 0] = to_godot(point_on_1);
+ debug_contacts.write[pair_index + 1] = to_godot(point_on_2);
+ }
+
+ return true;
+}
+
+bool JoltContactListener3D::_try_add_debug_contacts(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold) {
+ const int64_t max_count = debug_contacts.size();
+ if (max_count == 0) {
+ return false;
+ }
+
+ int additional_contacts = 0;
+
+ for (const JPH::SoftBodyVertex &vertex : p_manifold.GetVertices()) {
+ if (p_manifold.HasContact(vertex)) {
+ additional_contacts += 1;
+ }
+ }
+
+ int current_count = debug_contact_count.load(std::memory_order_relaxed);
+ bool exchanged = false;
+
+ do {
+ const int new_count = current_count + additional_contacts;
+
+ if (new_count > max_count) {
+ return false;
+ }
+
+ exchanged = debug_contact_count.compare_exchange_weak(current_count, new_count, std::memory_order_release, std::memory_order_relaxed);
+ } while (!exchanged);
+
+ int contact_index = current_count;
+
+ for (const JPH::SoftBodyVertex &vertex : p_manifold.GetVertices()) {
+ if (!p_manifold.HasContact(vertex)) {
+ continue;
+ }
+
+ const JPH::RMat44 body_com_transform = p_soft_body.GetCenterOfMassTransform();
+ const JPH::Vec3 local_contact_point = p_manifold.GetLocalContactPoint(vertex);
+ const JPH::RVec3 contact_point = body_com_transform * local_contact_point;
+
+ debug_contacts.write[contact_index++] = to_godot(contact_point);
+ }
+
+ return true;
+}
+
+#endif
+
+void JoltContactListener3D::_flush_contacts() {
+ for (auto &&[shape_pair, manifold] : manifolds_by_shape_pair) {
+ const JPH::BodyID body_ids[2] = { shape_pair.GetBody1ID(), shape_pair.GetBody2ID() };
+ const JoltReadableBodies3D jolt_bodies = space->read_bodies(body_ids, 2);
+
+ JoltBody3D *body1 = jolt_bodies[0].as_body();
+ ERR_FAIL_NULL(body1);
+
+ JoltBody3D *body2 = jolt_bodies[1].as_body();
+ ERR_FAIL_NULL(body2);
+
+ const int shape_index1 = body1->find_shape_index(shape_pair.GetSubShapeID1());
+ const int shape_index2 = body2->find_shape_index(shape_pair.GetSubShapeID2());
+
+ for (const Contact &contact : manifold.contacts1) {
+ body1->add_contact(body2, manifold.depth, shape_index1, shape_index2, to_godot(contact.normal), to_godot(contact.point_self), to_godot(contact.point_other), to_godot(contact.velocity_self), to_godot(contact.velocity_other), to_godot(contact.impulse));
+ }
+
+ for (const Contact &contact : manifold.contacts2) {
+ body2->add_contact(body1, manifold.depth, shape_index2, shape_index1, to_godot(contact.normal), to_godot(contact.point_self), to_godot(contact.point_other), to_godot(contact.velocity_self), to_godot(contact.velocity_other), to_godot(contact.impulse));
+ }
+
+ manifold.contacts1.clear();
+ manifold.contacts2.clear();
+ }
+}
+
+void JoltContactListener3D::_flush_area_enters() {
+ for (const JPH::SubShapeIDPair &shape_pair : area_enters) {
+ const JPH::BodyID &body_id1 = shape_pair.GetBody1ID();
+ const JPH::BodyID &body_id2 = shape_pair.GetBody2ID();
+
+ const JPH::SubShapeID &sub_shape_id1 = shape_pair.GetSubShapeID1();
+ const JPH::SubShapeID &sub_shape_id2 = shape_pair.GetSubShapeID2();
+
+ const JPH::BodyID body_ids[2] = { body_id1, body_id2 };
+ const JoltReadableBodies3D jolt_bodies = space->read_bodies(body_ids, 2);
+
+ const JoltReadableBody3D jolt_body1 = jolt_bodies[0];
+ const JoltReadableBody3D jolt_body2 = jolt_bodies[1];
+
+ if (jolt_body1.is_invalid() || jolt_body2.is_invalid()) {
+ continue;
+ }
+
+ JoltArea3D *area1 = jolt_body1.as_area();
+ JoltArea3D *area2 = jolt_body2.as_area();
+
+ if (area1 != nullptr && area2 != nullptr) {
+ area1->area_shape_entered(body_id2, sub_shape_id2, sub_shape_id1);
+ } else if (area1 != nullptr && area2 == nullptr) {
+ area1->body_shape_entered(body_id2, sub_shape_id2, sub_shape_id1);
+ } else if (area1 == nullptr && area2 != nullptr) {
+ area2->body_shape_entered(body_id1, sub_shape_id1, sub_shape_id2);
+ }
+ }
+
+ area_enters.clear();
+}
+
+void JoltContactListener3D::_flush_area_shifts() {
+ for (const JPH::SubShapeIDPair &shape_pair : area_overlaps) {
+ auto is_shifted = [&](const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_sub_shape_id) {
+ const JoltReadableBody3D jolt_body = space->read_body(p_body_id);
+ const JoltShapedObject3D *object = jolt_body.as_shaped();
+ ERR_FAIL_NULL_V(object, false);
+
+ if (object->get_previous_jolt_shape() == nullptr) {
+ return false;
+ }
+
+ const JPH::Shape ¤t_shape = *object->get_jolt_shape();
+ const JPH::Shape &previous_shape = *object->get_previous_jolt_shape();
+
+ const uint32_t current_id = (uint32_t)current_shape.GetSubShapeUserData(p_sub_shape_id);
+ const uint32_t previous_id = (uint32_t)previous_shape.GetSubShapeUserData(p_sub_shape_id);
+
+ return current_id != previous_id;
+ };
+
+ if (is_shifted(shape_pair.GetBody1ID(), shape_pair.GetSubShapeID1()) || is_shifted(shape_pair.GetBody2ID(), shape_pair.GetSubShapeID2())) {
+ area_enters.insert(shape_pair);
+ area_exits.insert(shape_pair);
+ }
+ }
+}
+
+void JoltContactListener3D::_flush_area_exits() {
+ for (const JPH::SubShapeIDPair &shape_pair : area_exits) {
+ const JPH::BodyID &body_id1 = shape_pair.GetBody1ID();
+ const JPH::BodyID &body_id2 = shape_pair.GetBody2ID();
+
+ const JPH::SubShapeID &sub_shape_id1 = shape_pair.GetSubShapeID1();
+ const JPH::SubShapeID &sub_shape_id2 = shape_pair.GetSubShapeID2();
+
+ const JPH::BodyID body_ids[2] = { body_id1, body_id2 };
+ const JoltReadableBodies3D jolt_bodies = space->read_bodies(body_ids, 2);
+
+ const JoltReadableBody3D jolt_body1 = jolt_bodies[0];
+ const JoltReadableBody3D jolt_body2 = jolt_bodies[1];
+
+ JoltArea3D *area1 = jolt_body1.as_area();
+ JoltArea3D *area2 = jolt_body2.as_area();
+
+ const JoltBody3D *body1 = jolt_body1.as_body();
+ const JoltBody3D *body2 = jolt_body2.as_body();
+
+ if (area1 != nullptr && area2 != nullptr) {
+ area1->area_shape_exited(body_id2, sub_shape_id2, sub_shape_id1);
+ } else if (area1 != nullptr && body2 != nullptr) {
+ area1->body_shape_exited(body_id2, sub_shape_id2, sub_shape_id1);
+ } else if (body1 != nullptr && area2 != nullptr) {
+ area2->body_shape_exited(body_id1, sub_shape_id1, sub_shape_id2);
+ } else if (area1 != nullptr) {
+ area1->shape_exited(body_id2, sub_shape_id2, sub_shape_id1);
+ } else if (area2 != nullptr) {
+ area2->shape_exited(body_id1, sub_shape_id1, sub_shape_id2);
+ }
+ }
+
+ area_exits.clear();
+}
+
+void JoltContactListener3D::listen_for(JoltShapedObject3D *p_object) {
+ listening_for.insert(p_object->get_jolt_id());
+}
+
+void JoltContactListener3D::pre_step() {
+ listening_for.clear();
+
+#ifdef DEBUG_ENABLED
+ debug_contact_count = 0;
+#endif
+}
+
+void JoltContactListener3D::post_step() {
+ _flush_contacts();
+ _flush_area_shifts();
+ _flush_area_exits();
+ _flush_area_enters();
+}
diff --git a/modules/jolt_physics/spaces/jolt_contact_listener_3d.h b/modules/jolt_physics/spaces/jolt_contact_listener_3d.h
new file mode 100644
index 000000000000..34f348140a8f
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_contact_listener_3d.h
@@ -0,0 +1,147 @@
+/**************************************************************************/
+/* jolt_contact_listener_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_CONTACT_LISTENER_3D_H
+#define JOLT_CONTACT_LISTENER_3D_H
+
+#include "core/templates/hash_map.h"
+#include "core/templates/hash_set.h"
+#include "core/templates/hashfuncs.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/safe_refcount.h"
+#include "core/variant/variant.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Body/Body.h"
+#include "Jolt/Physics/Collision/ContactListener.h"
+#include "Jolt/Physics/SoftBody/SoftBodyContactListener.h"
+
+#include
+#include
+
+class JoltShapedObject3D;
+class JoltSpace3D;
+
+class JoltContactListener3D final
+ : public JPH::ContactListener,
+ public JPH::SoftBodyContactListener {
+ struct BodyIDHasher {
+ static uint32_t hash(const JPH::BodyID &p_id) { return hash_fmix32(p_id.GetIndexAndSequenceNumber()); }
+ };
+
+ struct ShapePairHasher {
+ static uint32_t hash(const JPH::SubShapeIDPair &p_pair) {
+ uint32_t hash = hash_murmur3_one_32(p_pair.GetBody1ID().GetIndexAndSequenceNumber());
+ hash = hash_murmur3_one_32(p_pair.GetSubShapeID1().GetValue(), hash);
+ hash = hash_murmur3_one_32(p_pair.GetBody2ID().GetIndexAndSequenceNumber(), hash);
+ hash = hash_murmur3_one_32(p_pair.GetSubShapeID2().GetValue(), hash);
+ return hash_fmix32(hash);
+ }
+ };
+
+ struct Contact {
+ JPH::RVec3 point_self = JPH::RVec3::sZero();
+ JPH::RVec3 point_other = JPH::RVec3::sZero();
+ JPH::Vec3 normal = JPH::Vec3::sZero();
+ JPH::Vec3 velocity_self = JPH::Vec3::sZero();
+ JPH::Vec3 velocity_other = JPH::Vec3::sZero();
+ JPH::Vec3 impulse = JPH::Vec3::sZero();
+ };
+
+ typedef LocalVector Contacts;
+
+ struct Manifold {
+ Contacts contacts1;
+ Contacts contacts2;
+ float depth = 0.0f;
+ };
+
+ HashMap manifolds_by_shape_pair;
+ HashSet listening_for;
+ HashSet area_overlaps;
+ HashSet area_enters;
+ HashSet area_exits;
+ Mutex write_mutex;
+ JoltSpace3D *space = nullptr;
+
+#ifdef DEBUG_ENABLED
+ PackedVector3Array debug_contacts;
+ std::atomic_int debug_contact_count;
+#endif
+
+ virtual void OnContactAdded(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) override;
+ virtual void OnContactPersisted(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold, JPH::ContactSettings &p_settings) override;
+ 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 _is_listening_for(const JPH::Body &p_body) const;
+
+ 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_body1, const JPH::Body &p_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_remove_contacts(const JPH::SubShapeIDPair &p_shape_pair);
+ bool _try_remove_area_overlap(const JPH::SubShapeIDPair &p_shape_pair);
+
+#ifdef DEBUG_ENABLED
+ bool _try_add_debug_contacts(const JPH::Body &p_body1, const JPH::Body &p_body2, const JPH::ContactManifold &p_manifold);
+ bool _try_add_debug_contacts(const JPH::Body &p_soft_body, const JPH::SoftBodyManifold &p_manifold);
+#endif
+
+ void _flush_contacts();
+ void _flush_area_enters();
+ void _flush_area_shifts();
+ void _flush_area_exits();
+
+public:
+ explicit JoltContactListener3D(JoltSpace3D *p_space) :
+ space(p_space) {}
+
+ void listen_for(JoltShapedObject3D *p_object);
+
+ void pre_step();
+ void post_step();
+
+#ifdef DEBUG_ENABLED
+ const PackedVector3Array &get_debug_contacts() const { return debug_contacts; }
+ int get_debug_contact_count() const { return debug_contact_count.load(std::memory_order_acquire); }
+ int get_max_debug_contacts() const { return (int)debug_contacts.size(); }
+ void set_max_debug_contacts(int p_count) { debug_contacts.resize(p_count); }
+#endif
+};
+
+#endif // JOLT_CONTACT_LISTENER_3D_H
diff --git a/modules/jolt_physics/spaces/jolt_job_system.cpp b/modules/jolt_physics/spaces/jolt_job_system.cpp
new file mode 100644
index 000000000000..b4cae4fb133d
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_job_system.cpp
@@ -0,0 +1,203 @@
+/**************************************************************************/
+/* jolt_job_system.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_job_system.h"
+
+#include "../jolt_project_settings.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/error/error_macros.h"
+#include "core/math/math_defs.h"
+#include "core/object/worker_thread_pool.h"
+#include "core/os/os.h"
+#include "core/os/time.h"
+
+#include "Jolt/Physics/PhysicsSettings.h"
+
+void JoltJobSystem::Job::_execute(void *p_user_data) {
+ Job *job = static_cast(p_user_data);
+
+#ifdef DEBUG_ENABLED
+ const uint64_t time_start = Time::get_singleton()->get_ticks_usec();
+#endif
+
+ job->Execute();
+
+#ifdef DEBUG_ENABLED
+ const uint64_t time_end = Time::get_singleton()->get_ticks_usec();
+ const uint64_t time_elapsed = time_end - time_start;
+
+ timings_lock.lock();
+ timings_by_job[job->name] += time_elapsed;
+ timings_lock.unlock();
+#endif
+
+ job->Release();
+}
+
+JoltJobSystem::Job::Job(const char *p_name, JPH::ColorArg p_color, JPH::JobSystem *p_job_system, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count) :
+ JPH::JobSystem::Job(p_name, p_color, p_job_system, p_job_function, p_dependency_count)
+#ifdef DEBUG_ENABLED
+ ,
+ name(p_name)
+#endif
+{
+}
+
+JoltJobSystem::Job::~Job() {
+ if (task_id != -1) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(task_id);
+ }
+}
+
+void JoltJobSystem::Job::push_completed(Job *p_job) {
+ Job *prev_head = nullptr;
+
+ do {
+ prev_head = completed_head.load(std::memory_order_relaxed);
+ p_job->completed_next = prev_head;
+ } while (!completed_head.compare_exchange_weak(prev_head, p_job, std::memory_order_release, std::memory_order_relaxed));
+}
+
+JoltJobSystem::Job *JoltJobSystem::Job::pop_completed() {
+ Job *prev_head = nullptr;
+
+ do {
+ prev_head = completed_head.load(std::memory_order_relaxed);
+ if (prev_head == nullptr) {
+ return nullptr;
+ }
+ } while (!completed_head.compare_exchange_weak(prev_head, prev_head->completed_next, std::memory_order_acquire, std::memory_order_relaxed));
+
+ return prev_head;
+}
+
+void JoltJobSystem::Job::queue() {
+ AddRef();
+
+ // Ideally we would use Jolt's actual job name here, but I'd rather not incur the overhead of a memory allocation or
+ // thread-safe lookup every time we create/queue a task. So instead we use the same cached description for all of them.
+ static const String task_name("JoltPhysics");
+
+ task_id = WorkerThreadPool::get_singleton()->add_native_task(&_execute, this, true, task_name);
+}
+
+int JoltJobSystem::GetMaxConcurrency() const {
+ return thread_count;
+}
+
+JPH::JobHandle JoltJobSystem::CreateJob(const char *p_name, JPH::ColorArg p_color, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count) {
+ Job *job = nullptr;
+
+ while (true) {
+ JPH::uint32 job_index = jobs.ConstructObject(p_name, p_color, this, p_job_function, p_dependency_count);
+
+ if (job_index != JPH::FixedSizeFreeList::cInvalidObjectIndex) {
+ job = &jobs.Get(job_index);
+ break;
+ }
+
+ WARN_PRINT_ONCE("Jolt Physics job system exceeded the maximum number of jobs. This should not happen. Please report this. Waiting for jobs to become available...");
+
+ OS::get_singleton()->delay_usec(100);
+
+ _reclaim_jobs();
+ }
+
+ // This will increment the job's reference count, so must happen before we queue the job
+ JPH::JobHandle job_handle(job);
+
+ if (p_dependency_count == 0) {
+ QueueJob(job);
+ }
+
+ return job_handle;
+}
+
+void JoltJobSystem::QueueJob(JPH::JobSystem::Job *p_job) {
+ static_cast(p_job)->queue();
+}
+
+void JoltJobSystem::QueueJobs(JPH::JobSystem::Job **p_jobs, JPH::uint p_job_count) {
+ for (JPH::uint i = 0; i < p_job_count; ++i) {
+ QueueJob(p_jobs[i]);
+ }
+}
+
+void JoltJobSystem::FreeJob(JPH::JobSystem::Job *p_job) {
+ Job::push_completed(static_cast(p_job));
+}
+
+void JoltJobSystem::_reclaim_jobs() {
+ while (Job *job = Job::pop_completed()) {
+ jobs.DestructObject(job);
+ }
+}
+
+JoltJobSystem::JoltJobSystem() :
+ JPH::JobSystemWithBarrier(JPH::cMaxPhysicsBarriers),
+ thread_count(MAX(1, WorkerThreadPool::get_singleton()->get_thread_count())) {
+ jobs.Init(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsJobs);
+}
+
+void JoltJobSystem::pre_step() {
+ // Nothing to do.
+}
+
+void JoltJobSystem::post_step() {
+ _reclaim_jobs();
+}
+
+#ifdef DEBUG_ENABLED
+
+void JoltJobSystem::flush_timings() {
+ static const StringName profiler_name("servers");
+
+ EngineDebugger *engine_debugger = EngineDebugger::get_singleton();
+
+ if (engine_debugger->is_profiling(profiler_name)) {
+ Array timings;
+
+ for (auto &&[name, usec] : timings_by_job) {
+ timings.push_back(static_cast(name));
+ timings.push_back(USEC_TO_SEC(usec));
+ }
+
+ timings.push_front("physics_3d");
+
+ engine_debugger->profiler_add_frame_data(profiler_name, timings);
+ }
+
+ for (auto &&[name, usec] : timings_by_job) {
+ usec = 0;
+ }
+}
+
+#endif
diff --git a/modules/jolt_physics/spaces/jolt_job_system.h b/modules/jolt_physics/spaces/jolt_job_system.h
new file mode 100644
index 000000000000..573a8b174192
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_job_system.h
@@ -0,0 +1,107 @@
+/**************************************************************************/
+/* jolt_job_system.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_JOB_SYSTEM_H
+#define JOLT_JOB_SYSTEM_H
+
+#include "core/os/spin_lock.h"
+#include "core/templates/hash_map.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Core/FixedSizeFreeList.h"
+#include "Jolt/Core/JobSystemWithBarrier.h"
+
+#include
+#include
+
+class JoltJobSystem final : public JPH::JobSystemWithBarrier {
+ class Job : public JPH::JobSystem::Job {
+ inline static std::atomic completed_head = nullptr;
+
+#ifdef DEBUG_ENABLED
+ const char *name = nullptr;
+#endif
+
+ int64_t task_id = -1;
+
+ std::atomic completed_next = nullptr;
+
+ static void _execute(void *p_user_data);
+
+ public:
+ Job(const char *p_name, JPH::ColorArg p_color, JPH::JobSystem *p_job_system, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count);
+ Job(const Job &p_other) = delete;
+ Job(Job &&p_other) = delete;
+ ~Job();
+
+ static void push_completed(Job *p_job);
+ static Job *pop_completed();
+
+ void queue();
+
+ Job &operator=(const Job &p_other) = delete;
+ Job &operator=(Job &&p_other) = delete;
+ };
+
+#ifdef DEBUG_ENABLED
+ // We use `const void*` here to avoid the cost of hashing the actual string, since the job names
+ // are always literals and as such will point to the same address every time.
+ inline static HashMap timings_by_job;
+
+ // TODO: Check whether the usage of SpinLock is justified or if this should be a mutex instead.
+ inline static SpinLock timings_lock;
+#endif
+
+ JPH::FixedSizeFreeList jobs;
+
+ int thread_count = 0;
+
+ virtual int GetMaxConcurrency() const override;
+
+ virtual JPH::JobHandle CreateJob(const char *p_name, JPH::ColorArg p_color, const JPH::JobSystem::JobFunction &p_job_function, JPH::uint32 p_dependency_count = 0) override;
+ virtual void QueueJob(JPH::JobSystem::Job *p_job) override;
+ virtual void QueueJobs(JPH::JobSystem::Job **p_jobs, JPH::uint p_job_count) override;
+ virtual void FreeJob(JPH::JobSystem::Job *p_job) override;
+
+ void _reclaim_jobs();
+
+public:
+ JoltJobSystem();
+
+ void pre_step();
+ void post_step();
+
+#ifdef DEBUG_ENABLED
+ void flush_timings();
+#endif
+};
+
+#endif // JOLT_JOB_SYSTEM_H
diff --git a/modules/jolt_physics/spaces/jolt_layers.cpp b/modules/jolt_physics/spaces/jolt_layers.cpp
new file mode 100644
index 000000000000..c39c3a339702
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_layers.cpp
@@ -0,0 +1,223 @@
+/**************************************************************************/
+/* jolt_layers.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_layers.h"
+
+#include "../jolt_project_settings.h"
+#include "jolt_broad_phase_layer.h"
+
+#include "core/error/error_macros.h"
+#include "core/variant/variant.h"
+
+static_assert(sizeof(JPH::ObjectLayer) == 2, "Size of Jolt's object layer has changed.");
+static_assert(sizeof(JPH::BroadPhaseLayer::Type) == 1, "Size of Jolt's broadphase layer has changed.");
+static_assert(JoltBroadPhaseLayer::COUNT <= 8, "Maximum number of broadphase layers exceeded.");
+
+namespace {
+
+template
+class JoltBroadPhaseMatrix {
+ typedef JPH::BroadPhaseLayer LayerType;
+ typedef LayerType::Type UnderlyingType;
+
+public:
+ JoltBroadPhaseMatrix() {
+ using namespace JoltBroadPhaseLayer;
+
+ allow_collision(BODY_STATIC, BODY_DYNAMIC);
+ allow_collision(BODY_STATIC_BIG, BODY_DYNAMIC);
+ allow_collision(BODY_DYNAMIC, BODY_STATIC);
+ allow_collision(BODY_DYNAMIC, BODY_STATIC_BIG);
+ allow_collision(BODY_DYNAMIC, BODY_DYNAMIC);
+ allow_collision(BODY_DYNAMIC, AREA_DETECTABLE);
+ allow_collision(BODY_DYNAMIC, AREA_UNDETECTABLE);
+ allow_collision(AREA_DETECTABLE, BODY_DYNAMIC);
+ allow_collision(AREA_DETECTABLE, AREA_DETECTABLE);
+ allow_collision(AREA_DETECTABLE, AREA_UNDETECTABLE);
+ allow_collision(AREA_UNDETECTABLE, BODY_DYNAMIC);
+ allow_collision(AREA_UNDETECTABLE, AREA_DETECTABLE);
+
+ if (JoltProjectSettings::areas_detect_static_bodies()) {
+ allow_collision(BODY_STATIC, AREA_DETECTABLE);
+ allow_collision(BODY_STATIC, AREA_UNDETECTABLE);
+ allow_collision(BODY_STATIC_BIG, AREA_DETECTABLE);
+ allow_collision(BODY_STATIC_BIG, AREA_UNDETECTABLE);
+ allow_collision(AREA_DETECTABLE, BODY_STATIC);
+ allow_collision(AREA_DETECTABLE, BODY_STATIC_BIG);
+ allow_collision(AREA_UNDETECTABLE, BODY_STATIC);
+ allow_collision(AREA_UNDETECTABLE, BODY_STATIC_BIG);
+ }
+ }
+
+ void allow_collision(UnderlyingType p_layer1, UnderlyingType p_layer2) { masks[p_layer1] |= uint8_t(1U << p_layer2); }
+ void allow_collision(LayerType p_layer1, LayerType p_layer2) { allow_collision((UnderlyingType)p_layer1, (UnderlyingType)p_layer2); }
+
+ bool should_collide(UnderlyingType p_layer1, UnderlyingType p_layer2) const { return (masks[p_layer1] & uint8_t(1U << p_layer2)) != 0; }
+ bool should_collide(LayerType p_layer1, LayerType p_layer2) const { return should_collide((UnderlyingType)p_layer1, (UnderlyingType)p_layer2); }
+
+private:
+ uint8_t masks[TSize] = {};
+};
+
+constexpr JPH::ObjectLayer encode_layers(JPH::BroadPhaseLayer p_broad_phase_layer, JPH::ObjectLayer p_object_layer) {
+ const uint16_t upper_bits = uint16_t((uint8_t)p_broad_phase_layer << 13U);
+ const uint16_t lower_bits = uint16_t(p_object_layer);
+ return JPH::ObjectLayer(upper_bits | lower_bits);
+}
+
+constexpr void decode_layers(JPH::ObjectLayer p_encoded_layers, JPH::BroadPhaseLayer &r_broad_phase_layer, JPH::ObjectLayer &r_object_layer) {
+ r_broad_phase_layer = JPH::BroadPhaseLayer(uint8_t(p_encoded_layers >> 13U));
+ r_object_layer = JPH::ObjectLayer(p_encoded_layers & 0b0001'1111'1111'1111U);
+}
+
+constexpr uint64_t encode_collision(uint32_t p_collision_layer, uint32_t p_collision_mask) {
+ const uint64_t upper_bits = (uint64_t)p_collision_layer << 32U;
+ const uint64_t lower_bits = (uint64_t)p_collision_mask;
+ return upper_bits | lower_bits;
+}
+
+constexpr void decode_collision(uint64_t p_collision, uint32_t &r_collision_layer, uint32_t &r_collision_mask) {
+ r_collision_layer = uint32_t(p_collision >> 32U);
+ r_collision_mask = uint32_t(p_collision & 0xFFFFFFFFU);
+}
+
+} // namespace
+
+uint32_t JoltLayers::GetNumBroadPhaseLayers() const {
+ return JoltBroadPhaseLayer::COUNT;
+}
+
+JPH::BroadPhaseLayer JoltLayers::GetBroadPhaseLayer(JPH::ObjectLayer p_layer) const {
+ JPH::BroadPhaseLayer broad_phase_layer = JoltBroadPhaseLayer::BODY_STATIC;
+ JPH::ObjectLayer object_layer = 0;
+ decode_layers(p_layer, broad_phase_layer, object_layer);
+
+ return broad_phase_layer;
+}
+
+#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+
+const char *JoltLayers::GetBroadPhaseLayerName(JPH::BroadPhaseLayer p_layer) const {
+ switch ((JPH::BroadPhaseLayer::Type)p_layer) {
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC: {
+ return "BODY_STATIC";
+ }
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC_BIG: {
+ return "BODY_STATIC_BIG";
+ }
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_DYNAMIC: {
+ return "BODY_DYNAMIC";
+ }
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_DETECTABLE: {
+ return "AREA_DETECTABLE";
+ }
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_UNDETECTABLE: {
+ return "AREA_UNDETECTABLE";
+ }
+ default: {
+ return "UNKNOWN";
+ }
+ }
+}
+
+#endif
+
+bool JoltLayers::ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::ObjectLayer p_encoded_layer2) const {
+ JPH::BroadPhaseLayer broad_phase_layer1 = JoltBroadPhaseLayer::BODY_STATIC;
+ uint32_t collision_layer1 = 0;
+ uint32_t collision_mask1 = 0;
+ from_object_layer(p_encoded_layer1, broad_phase_layer1, collision_layer1, collision_mask1);
+
+ JPH::BroadPhaseLayer broad_phase_layer2 = JoltBroadPhaseLayer::BODY_STATIC;
+ uint32_t collision_layer2 = 0;
+ uint32_t collision_mask2 = 0;
+ from_object_layer(p_encoded_layer2, broad_phase_layer2, collision_layer2, collision_mask2);
+
+ const bool first_scans_second = (collision_mask1 & collision_layer2) != 0;
+ const bool second_scans_first = (collision_mask2 & collision_layer1) != 0;
+
+ return first_scans_second || second_scans_first;
+}
+
+bool JoltLayers::ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::BroadPhaseLayer p_broad_phase_layer2) const {
+ static const JoltBroadPhaseMatrix matrix;
+
+ JPH::BroadPhaseLayer broad_phase_layer1 = JoltBroadPhaseLayer::BODY_STATIC;
+ JPH::ObjectLayer object_layer1 = 0;
+ decode_layers(p_encoded_layer1, broad_phase_layer1, object_layer1);
+
+ return matrix.should_collide(broad_phase_layer1, p_broad_phase_layer2);
+}
+
+JPH::ObjectLayer JoltLayers::_allocate_object_layer(uint64_t p_collision) {
+ const JPH::ObjectLayer new_object_layer = next_object_layer++;
+
+ collisions_by_layer.resize(new_object_layer + 1);
+ collisions_by_layer[new_object_layer] = p_collision;
+
+ layers_by_collision[p_collision] = new_object_layer;
+
+ return new_object_layer;
+}
+
+JoltLayers::JoltLayers() {
+ _allocate_object_layer(0);
+}
+
+JPH::ObjectLayer JoltLayers::to_object_layer(JPH::BroadPhaseLayer p_broad_phase_layer, uint32_t p_collision_layer, uint32_t p_collision_mask) {
+ const uint64_t collision = encode_collision(p_collision_layer, p_collision_mask);
+
+ JPH::ObjectLayer object_layer = 0;
+
+ HashMap::Iterator iter = layers_by_collision.find(collision);
+ if (iter != layers_by_collision.end()) {
+ object_layer = iter->value;
+ } else {
+ constexpr uint16_t object_layer_count = 1U << 13U;
+
+ ERR_FAIL_COND_V_MSG(next_object_layer == object_layer_count, 0,
+ vformat("Maximum number of object layers (%d) reached. "
+ "This means there are %d combinations of collision layers and masks."
+ "This should not happen under normal circumstances. Consider reporting this.",
+ object_layer_count, object_layer_count));
+
+ object_layer = _allocate_object_layer(collision);
+ }
+
+ return encode_layers(p_broad_phase_layer, object_layer);
+}
+
+void JoltLayers::from_object_layer(JPH::ObjectLayer p_encoded_layer, JPH::BroadPhaseLayer &r_broad_phase_layer, uint32_t &r_collision_layer, uint32_t &r_collision_mask) const {
+ JPH::ObjectLayer object_layer = 0;
+ decode_layers(p_encoded_layer, r_broad_phase_layer, object_layer);
+
+ const uint64_t collision = collisions_by_layer[object_layer];
+ decode_collision(collision, r_collision_layer, r_collision_mask);
+}
diff --git a/modules/jolt_physics/spaces/jolt_layers.h b/modules/jolt_physics/spaces/jolt_layers.h
new file mode 100644
index 000000000000..f610477a6650
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_layers.h
@@ -0,0 +1,71 @@
+/**************************************************************************/
+/* jolt_layers.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_LAYERS_H
+#define JOLT_LAYERS_H
+
+#include "core/templates/hash_map.h"
+#include "core/templates/local_vector.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
+#include "Jolt/Physics/Collision/ObjectLayer.h"
+
+#include
+
+class JoltLayers final
+ : public JPH::BroadPhaseLayerInterface,
+ public JPH::ObjectLayerPairFilter,
+ public JPH::ObjectVsBroadPhaseLayerFilter {
+ LocalVector collisions_by_layer;
+ HashMap layers_by_collision;
+ JPH::ObjectLayer next_object_layer = 0;
+
+ virtual uint32_t GetNumBroadPhaseLayers() const override;
+ virtual JPH::BroadPhaseLayer GetBroadPhaseLayer(JPH::ObjectLayer p_layer) const override;
+
+#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+ virtual const char *GetBroadPhaseLayerName(JPH::BroadPhaseLayer p_layer) const override;
+#endif
+
+ virtual bool ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::ObjectLayer p_encoded_layer2) const override;
+ virtual bool ShouldCollide(JPH::ObjectLayer p_encoded_layer1, JPH::BroadPhaseLayer p_broad_phase_layer2) const override;
+
+ JPH::ObjectLayer _allocate_object_layer(uint64_t p_collision);
+
+public:
+ JoltLayers();
+
+ JPH::ObjectLayer to_object_layer(JPH::BroadPhaseLayer p_broad_phase_layer, uint32_t p_collision_layer, uint32_t p_collision_mask);
+ void from_object_layer(JPH::ObjectLayer p_encoded_layer, JPH::BroadPhaseLayer &r_broad_phase_layer, uint32_t &r_collision_layer, uint32_t &r_collision_mask) const;
+};
+
+#endif // JOLT_LAYERS_H
diff --git a/modules/jolt_physics/spaces/jolt_motion_filter_3d.cpp b/modules/jolt_physics/spaces/jolt_motion_filter_3d.cpp
new file mode 100644
index 000000000000..7175a89c4d99
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_motion_filter_3d.cpp
@@ -0,0 +1,116 @@
+/**************************************************************************/
+/* jolt_motion_filter_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_motion_filter_3d.h"
+
+#include "../objects/jolt_body_3d.h"
+#include "../objects/jolt_object_3d.h"
+#include "../shapes/jolt_custom_motion_shape.h"
+#include "../shapes/jolt_custom_ray_shape.h"
+#include "../shapes/jolt_custom_shape_type.h"
+#include "../shapes/jolt_shape_3d.h"
+#include "jolt_broad_phase_layer.h"
+#include "jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+JoltMotionFilter3D::JoltMotionFilter3D(const JoltBody3D &p_body, const HashSet &p_excluded_bodies, const HashSet &p_excluded_objects, bool p_collide_separation_ray) :
+ body_self(p_body),
+ space(*body_self.get_space()),
+ excluded_bodies(p_excluded_bodies),
+ excluded_objects(p_excluded_objects),
+ collide_separation_ray(p_collide_separation_ray) {
+}
+
+bool JoltMotionFilter3D::ShouldCollide(JPH::BroadPhaseLayer p_broad_phase_layer) const {
+ const JPH::BroadPhaseLayer::Type broad_phase_layer = (JPH::BroadPhaseLayer::Type)p_broad_phase_layer;
+
+ switch (broad_phase_layer) {
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC:
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_STATIC_BIG:
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::BODY_DYNAMIC: {
+ return true;
+ } break;
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_DETECTABLE:
+ case (JPH::BroadPhaseLayer::Type)JoltBroadPhaseLayer::AREA_UNDETECTABLE: {
+ return false;
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(false, vformat("Unhandled broad phase layer: '%d'. This should not happen. Please report this.", broad_phase_layer));
+ }
+ }
+}
+
+bool JoltMotionFilter3D::ShouldCollide(JPH::ObjectLayer p_object_layer) const {
+ JPH::BroadPhaseLayer object_broad_phase_layer = JoltBroadPhaseLayer::BODY_STATIC;
+ uint32_t object_collision_layer = 0;
+ uint32_t object_collision_mask = 0;
+
+ space.map_from_object_layer(p_object_layer, object_broad_phase_layer, object_collision_layer, object_collision_mask);
+
+ return (body_self.get_collision_mask() & object_collision_layer) != 0;
+}
+
+bool JoltMotionFilter3D::ShouldCollide(const JPH::BodyID &p_jolt_id) const {
+ return p_jolt_id != body_self.get_jolt_id();
+}
+
+bool JoltMotionFilter3D::ShouldCollideLocked(const JPH::Body &p_jolt_body) const {
+ if (p_jolt_body.IsSoftBody()) {
+ return false;
+ }
+
+ const JoltObject3D *object = reinterpret_cast(p_jolt_body.GetUserData());
+ if (excluded_objects.has(object->get_instance_id()) || excluded_bodies.has(object->get_rid())) {
+ return false;
+ }
+
+ const JoltReadableBody3D jolt_body_self = space.read_body(body_self);
+ return jolt_body_self->GetCollisionGroup().CanCollide(p_jolt_body.GetCollisionGroup());
+}
+
+bool JoltMotionFilter3D::ShouldCollide(const JPH::Shape *p_jolt_shape, const JPH::SubShapeID &p_jolt_shape_id) const {
+ return true;
+}
+
+bool JoltMotionFilter3D::ShouldCollide(const JPH::Shape *p_jolt_shape_self, const JPH::SubShapeID &p_jolt_shape_id_self, const JPH::Shape *p_jolt_shape_other, const JPH::SubShapeID &p_jolt_shape_id_other) const {
+ if (collide_separation_ray) {
+ return true;
+ }
+
+ const JoltCustomMotionShape *motion_shape = static_cast(p_jolt_shape_self);
+ const JPH::ConvexShape &actual_shape_self = motion_shape->get_inner_shape();
+ if (actual_shape_self.GetSubType() == JoltCustomShapeSubType::RAY) {
+ // When `slide_on_slope` is enabled the ray shape acts as a regular shape.
+ return static_cast(actual_shape_self).slide_on_slope;
+ }
+
+ return true;
+}
diff --git a/modules/jolt_physics/spaces/jolt_motion_filter_3d.h b/modules/jolt_physics/spaces/jolt_motion_filter_3d.h
new file mode 100644
index 000000000000..1a3bd6379f2f
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_motion_filter_3d.h
@@ -0,0 +1,72 @@
+/**************************************************************************/
+/* jolt_motion_filter_3d.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef JOLT_MOTION_FILTER_3D_H
+#define JOLT_MOTION_FILTER_3D_H
+
+#include "core/object/object_id.h"
+#include "core/templates/hash_set.h"
+#include "core/templates/rid.h"
+
+#include "Jolt/Jolt.h"
+
+#include "Jolt/Physics/Body/Body.h"
+#include "Jolt/Physics/Body/BodyFilter.h"
+#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h"
+#include "Jolt/Physics/Collision/ObjectLayer.h"
+#include "Jolt/Physics/Collision/ShapeFilter.h"
+
+class JoltBody3D;
+class JoltPhysicsServer3D;
+class JoltSpace3D;
+
+class JoltMotionFilter3D final
+ : public JPH::BroadPhaseLayerFilter,
+ public JPH::ObjectLayerFilter,
+ public JPH::BodyFilter,
+ public JPH::ShapeFilter {
+ const JoltBody3D &body_self;
+ const JoltSpace3D &space;
+ const HashSet &excluded_bodies;
+ const HashSet &excluded_objects;
+ bool collide_separation_ray = false;
+
+public:
+ explicit JoltMotionFilter3D(const JoltBody3D &p_body, const HashSet &p_excluded_bodies, const HashSet &p_excluded_objects, bool p_collide_separation_ray = true);
+
+ virtual bool ShouldCollide(JPH::BroadPhaseLayer p_broad_phase_layer) const override;
+ virtual bool ShouldCollide(JPH::ObjectLayer p_object_layer) const override;
+ virtual bool ShouldCollide(const JPH::BodyID &p_jolt_id) const override;
+ virtual bool ShouldCollideLocked(const JPH::Body &p_jolt_body) const override;
+ virtual bool ShouldCollide(const JPH::Shape *p_jolt_shape, const JPH::SubShapeID &p_jolt_shape_id) const override;
+ virtual bool ShouldCollide(const JPH::Shape *p_jolt_shape_self, const JPH::SubShapeID &p_jolt_shape_id_self, const JPH::Shape *p_jolt_shape_other, const JPH::SubShapeID &p_jolt_shape_id_other) const override;
+};
+
+#endif // JOLT_MOTION_FILTER_3D_H
diff --git a/modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp b/modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp
new file mode 100644
index 000000000000..285ae1dd8670
--- /dev/null
+++ b/modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp
@@ -0,0 +1,940 @@
+/**************************************************************************/
+/* jolt_physics_direct_space_state_3d.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "jolt_physics_direct_space_state_3d.h"
+
+#include "../jolt_physics_server_3d.h"
+#include "../jolt_project_settings.h"
+#include "../misc/jolt_type_conversions.h"
+#include "../objects/jolt_area_3d.h"
+#include "../objects/jolt_body_3d.h"
+#include "../objects/jolt_object_3d.h"
+#include "../shapes/jolt_custom_motion_shape.h"
+#include "../shapes/jolt_shape_3d.h"
+#include "jolt_motion_filter_3d.h"
+#include "jolt_query_collectors.h"
+#include "jolt_query_filter_3d.h"
+#include "jolt_space_3d.h"
+
+#include "core/error/error_macros.h"
+
+#include "Jolt/Geometry/GJKClosestPoint.h"
+#include "Jolt/Physics/Body/Body.h"
+#include "Jolt/Physics/Body/BodyFilter.h"
+#include "Jolt/Physics/Collision/BroadPhase/BroadPhaseQuery.h"
+#include "Jolt/Physics/Collision/CastResult.h"
+#include "Jolt/Physics/Collision/CollidePointResult.h"
+#include "Jolt/Physics/Collision/NarrowPhaseQuery.h"
+#include "Jolt/Physics/Collision/RayCast.h"
+#include "Jolt/Physics/Collision/Shape/MeshShape.h"
+#include "Jolt/Physics/PhysicsSystem.h"
+
+bool JoltPhysicsDirectSpaceState3D::_cast_motion_impl(const JPH::Shape &p_jolt_shape, const Transform3D &p_transform_com, const Vector3 &p_scale, const Vector3 &p_motion, bool p_use_edge_removal, bool p_ignore_overlaps, const JPH::CollideShapeSettings &p_settings, const JPH::BroadPhaseLayerFilter &p_broad_phase_layer_filter, const JPH::ObjectLayerFilter &p_object_layer_filter, const JPH::BodyFilter &p_body_filter, const JPH::ShapeFilter &p_shape_filter, real_t &r_closest_safe, real_t &r_closest_unsafe) const {
+ r_closest_safe = 1.0f;
+ r_closest_unsafe = 1.0f;
+
+ ERR_FAIL_COND_V_MSG(p_jolt_shape.GetType() != JPH::EShapeType::Convex, false, "Shape-casting with non-convex shapes is not supported.");
+
+ const float motion_length = (float)p_motion.length();
+
+ if (p_ignore_overlaps && motion_length == 0.0f) {
+ return false;
+ }
+
+ const JPH::RMat44 transform_com = to_jolt_r(p_transform_com);
+ const JPH::Vec3 scale = to_jolt(p_scale);
+ const JPH::Vec3 motion = to_jolt(p_motion);
+ const JPH::Vec3 motion_local = transform_com.Multiply3x3Transposed(motion);
+
+ JPH::AABox aabb = p_jolt_shape.GetWorldSpaceBounds(transform_com, scale);
+ JPH::AABox aabb_translated = aabb;
+ aabb_translated.Translate(motion);
+ aabb.Encapsulate(aabb_translated);
+
+ JoltQueryCollectorAnyMulti aabb_collector;
+ space->get_broad_phase_query().CollideAABox(aabb, aabb_collector, p_broad_phase_layer_filter, p_object_layer_filter);
+
+ if (!aabb_collector.had_hit()) {
+ return false;
+ }
+
+ const JPH::RVec3 base_offset = transform_com.GetTranslation();
+
+ JoltCustomMotionShape motion_shape(static_cast(p_jolt_shape));
+
+ auto collides = [&](const JPH::Body &p_other_body, float p_fraction) {
+ motion_shape.set_motion(motion_local * p_fraction);
+
+ const JPH::TransformedShape other_shape = p_other_body.GetTransformedShape();
+
+ JoltQueryCollectorAny