Add Jolt Physics as an alternative 3D physics engine#99895
Add Jolt Physics as an alternative 3D physics engine#99895akien-mga merged 1 commit intogodotengine:masterfrom
Conversation
d43694f to
dc9a08e
Compare
|
Looking good to me! The diff between godot-jolt and this module is much bigger than I anticipated, must have been a lot of work! |
|
Will the addon discontinue and only this will continue? |
Yeah, I thought I had a somewhat Godot-like codebase already, but getting rid of all my not-so-Godot idiosyncrasies was a lot of work indeed. I'm sure there's a few left that I've missed.
Thanks, I'll take a look!
Assuming this module is actually merged and reaches some kind of feature parity with the extension, yes. I don't see the need to maintain both at that point.
I'll admit I had forgotten that the physics tests in the demo projects repository even existed, but yeah, they seem fine. Although the performance tests seem to crash with either engine for some reason. I did run the tests in fabriceci/Godot-Physics-Tests beforehand though, and they seem to report the same failures that I've seen before: The collision normal generated in this test is indeed slightly off when using Jolt, but I'm not sure it's worth getting hung up on, especially considering how dense the convex polygon is. The test is also done using These fail because the impulse is applied before the body has been added to the physics space, meaning there's no underlying Jolt body to actually apply the impulse to, which I emit an error for currently. You can make these tests pass if you just swap these two lines around. The problem is you need to calculate the mass properties in order to apply impulses correctly, which can be expensive. In fact, Godot Physics defers this calculation as well, but still lets you apply impulses, resulting in incorrect behavior, as seen in #75934. This fails because the position error exceeds the tolerance by ~0.006 units. I'm not sure it's worth getting hung up on. It's possible that this is caused/worsened by one of the several discrepancies in Stacked spheres do seem to oscillate more in Jolt, even when setting the Baumgarte stabilization to |
|
Great job mihe, jrouwe, and everyone else involved. I build this and master and spend some time testing both by running the jolt module and godot physics in parallel on the same scenes and projects. So far I am pleasantly surprised that it already works so well considering that so much code had to be changed for the module. I did not encounter anything that I would really worry about, no error spam or really broken behavior issues swapping the physics in any of my test projects. In fact a few things that I knew were a little brittle and easy to make explode felt a lot more robust with the jolt module. Yes I encountered a few random issues but ad hoc I am not sure if they are even caused by the physics or just some weird editor or script code. Need to investigate and test some more. Having the two physics run next to each other it was difficult to spot an actual difference in most situations. The most noticeable for me was the joint behavior. For some reason the joints in the "official" Godot physics tests demo project loved to spin in circles when pushed or moved with the jolt module. Do you know what would cause such a difference in behavior compared to the godot physics and if anything can be done about it? Here is the link for the demo git (or asset lib was already posted). The "official" Godot physics test demo showed some other issues. It actually crashes in the performance test of that demo as the jolt body count of I would recommend trying out all those tests in that demo project so the most "obvious" things can be ironed out before this gets into more hands. I am sure there will be some other things only discovered by more broad user testing as is always the case. I think that will be unavoidable by such a huge module change and why there will be both physics for some time to fix and adapt.
If you forward the rendering scenario RID of the viewport to the jolt module for debug rendering it can add whatever it wants with the RenderingServer API without actually "hacking" itself into the Viewport code. Users could even render the entire debug in a different debug viewport this way by just switching viewport scenarios or split things up. The current physics partly hacks its collision contacts inside Viewport nodes which is nothing but ugly. Same with the node based debug shape rendering that omits all server created objects and does not run in sync with the actual physics step and state. So those likely will need settings to not have both debugs flicker over each other.
Yes there are some tiny differences in the return values between the two physics or in general but seriously we are talking about an "error" margin that I find totally acceptable and negatable in context of game engine physics. I know some people can be really riled up about such details but a friendly reminder this is a game engine and not a scientific simulation, the physics need to work in game engine context. It is not about simulating stuff so we can shoot and bounce stuff with pinpoint accuracy to the moon, it is about making games work without the physics exploding left, right and center at every shape seam or step while also not running at 1 fps ... or at least that should imo be the "humble" goal before we worry about those other things. |
Please do try to share repros of whatever issues you run into.
EDIT: I misread. I'll take a look.
I had glossed over this due to Godot Physics also crashing for me in the broadphase performance test (which seems to be from a stack overflow) so I figured it was something with the test itself, but it seems that the crash with this module was indeed a sloppy regression on my part. That's been fixed now, and you should instead get a "graceful" outcome in the form of error spam about needing to increase the body limit. |
@ryevdokimov You should find that this works now. |
Is this still true regarding shape creation as well? |
Looks good, I'll mark the comment I made as resolved. |
|
This is very cool! My thanks goes out to @mihe for spending so much time in making this plugin. Jolt really improved due to all the issues he found and requests he made! It has been a joy working together! Looking forward to the last push to make it production ready! Feel free to tag me on any issues that are reported. Also it would be nice if there was a label that I can filter on to quickly find issues reported against this module, e.g. |
We could add this easily, but it would be quite specific compared to our other labels which typically encompass whole systems (like We do have a need for more granularity but labels won't cut it (we'd need to multiply the amount of labels by 3-5 to cover everything), instead we're working on preparing some public GitHub projects for each team/system (basically each |
|
First of all. Nice work. One thing that would be a game changer for my game project, which heavily relies on modifying compound, rigid bodies with sometimes hundreds of shapes would be to be able to offload the updating process of these bodies to a worker thread. This would cause the body, and then replacing the body a few frames later. This would greatly reduce the risk of frame rate drops upon adding or removing shapes to a body. The trade-off to wait for a frame or two to get the new shape in place would in this case be more than reasonable in my opinion. This could practically be achieved through adding an opt-in bool parameter in the PhysicsServer3D.body_[add/remove]_shape or by a project setting. Would you @mihe think this would be an impossibility? On another note. Will Godot's 64-bit precision option work within this framework? How will Godot integrate Jolt's ability to work with vector instructions (SSE, AVX etc)? |
It should: godot/modules/jolt_physics/SCsub Line 155 in b9437c3 See also about how it was added: jrouwe/JoltPhysics#94 (comment) |
|
Is the common/physics_interpolation function of Godot adapted to JOLT?@mihe |
Yes, physics interpolation works out of the box with Jolt. It's not performed in the physics engine, but within the rest of the engine, so it works with third-party physics engines too. It'll even work if you write your own physics in |
|
I suppose jolt being now part of the Godot engine, I should file bug reports for it in Godot Engine's issues, right? |
To be clear, the Jolt project is still a standalone library that has its own development priorities and issue tracker. If you have issues with Jolt in Godot, then yes those issues should be filed in the Godot repository, so that we can assess whether the problem comes from the Godot integration, or is a library issue that needs to bubble up upstream. |
To add to this, if the bug is manifesting with the Godot Jolt extension then you're welcome to continue reporting them to the If the bug is manifesting in this new engine module, then definitely report it to the Godot repository.
@albinaask This discussions is probably better suited for a proposal, but it's certainly not impossible. The question is more of whether there are other approaches that can help alleviate this without burdening the shared
@albinaask As far as I know, Jolt should automatically make use of whatever instruction sets are enabled/defined during compilation, which for Godot is SSE2 by default. If you want more than this (SSE4.2, AVX, etc.) you'll need to pass the appropriate compiler options (e.g. |
|
Thats a great news♥ Thank a lot for your hard work. Looking foward to benchmark this a lot. :) |
It's not clear what exactly defines a scale as invalid, we have been seeing errors of Jolt complaining about scales like (1.2, 1.8, 1.2). Given those limitations, I recommend that the godot-jolt docs should advise against rigidbody scaling until this is sorted out. |
|
It is well defined what scales are allowed. Scales are allowed as long as they don't introduce shear / change the shape to another type of shape. So:
|
This hardly seems like an improvement over the default physics engine. |
Godot Physics doesn't support scaling Godot Physics has a lot of the same (and I believe even more) limitations for everything else when it comes to scaling, which is why you see a node configuration warning (in the Scene dock) if you try to do any type of non-uniform scaling of something like a The major difference is that Godot Physics doesn't bother with any checking to see if you're actually making these mistakes at runtime, which can result in a significant disparity between the actual collision detection and what you see/expect, which we felt warranted an error. As mentioned in the PR description here, I realize this is a point of friction for some, and is something that I've been wanting to address in some better way, but what that would be isn't exactly clear, nor have I really had the time to explore other options. There is of course a reasonable argument for just conforming to Godot Physics and removing this runtime check altogether, letting this invalid scaling happen silently, but frankly I haven't seen enough people complain about this yet to go that far. Anyway, I've just set up a dedicated issue for this, so we can continue the discussion there instead: #107986 In the meantime I'll add some more details about this to the documentation. |
Only good solution to this is if you scale a sphere, internally it would need to convert the sphere to an ellipse. Then for if you scale an elipse and its no longer an ellipse, it needs to be converted to another shape. It's not impossible to do, just it also has downsides. Depending on how the shape is converted, it might be trickier to compute mass of it (eg. if the shape cannot be easily decomposed to convex polygons, etc.). However it's definitely possible. On another note, I think parry(the lib rapier uses internally) has this internally (https://github.com/dimforge/parry/blob/fd61591cf881436faa3e99385f4947b1b942d9fe/src/shape/ball.rs#L64 , similar for other shapes). Some code can be reused from there if wanted, but it's definitely complicated and not trivial to do. Also it would require changing the shape while game runs (if you change scale at runtime). So a lot of implications. Also in godot-rapier I have some code that works with this, but it's only because rapier already exposes a lot and I mean a lot of helper fucntions for this: https://github.com/appsinacup/godot-rapier-physics/blob/14859d3cd4489236deade9ed379de9301722908e/src/rapier_wrapper/collider.rs#L84 |
The Jolt engine should give the game better performance and consistency. Per the PR where it was added to Godot godotengine/godot#99895 "Users of this new module should (generally, but not always) be able to expect better physics performance, both in terms of CPU performance and simulation stability, when compared to Godot Physics." The Jolt physics engine is available as an optional built-in as of Godot 4.4, but has to be explicitly enabled. When making the change in the Godot editor it shows a warning saying: "Changed settings will be applied to the editor after restarting." Since this change will modify the project file from outside of the editor, it will not give you the same warning. When you test this change, after checking out this branch, make sure you manually reload the project so that Godot is actually using the Jolt physics engine. Reference: Using Jolt Physics from Godot docs https://docs.godotengine.org/en/4.5/tutorials/physics/using_jolt_physics.html
Closes godotengine/godot-proposals#7308.
This pull request adds a new engine module (found under
modules/jolt_physics) which integrates Jolt Physics, a 3D physics engine developed by Jorrit Rouwe (@jrouwe) with a focus on games and VR applications, used in titles such as Horizon Forbidden West.Users of this new module should (generally, but not always) be able to expect better physics performance, both in terms of CPU performance and simulation stability, when compared to Godot Physics.
This new module is a port of the Godot Jolt extension, developed by myself over the past two years, in large part as a personal project, but with the last year (including this port) being sponsored by W4 Games. I've also received a significant amount of assistance and contributions from @jrouwe during this development, which I'm very grateful for. In fact, several major features have been added to Jolt largely to cater to the needs of Godot.
Warning
Note that while this code has gone through a fair amount of battle-testing as part of the Godot Jolt extension there have been non-trivial refactorings done as part of this port that have not been tested much at all. As such, this module should be considered experimental for now. This module is also not at feature parity with Godot Physics just yet, nor at feature parity with the Godot Jolt extension either.
This does not replace Godot Physics as the default 3D physics engine. You will need to opt in by setting the
physics/3d/physics_engineproject setting toJolt Physics.Notable differences to Godot Physics
While the goal is to have this new module be a drop-in replacement for Godot Physics (within reason), Jolt inherently differs from it in a number of ways, which means that it is unlikely that you will see the exact same behavior when switching between the two. There are also several higher-level decisions taken within the module that deviate further from how Godot Physics does things.
Area3Dand static bodies[Click to expand/collapse]
When using Jolt,
Area3Dwill not detect overlaps withStaticBody3D(nor aRigidBody3Dfrozen withFREEZE_MODE_STATIC) by default, for performance reasons. If you have many/largeArea3Doverlapping with complex static geometry, such asConcavePolygonShape3DorHeightMapShape3D, you can end up wasting a significant amount of CPU performance without realizing it.For this reason this behavior is opt-in through the project setting
physics/jolt_physics_3d/simulation/areas_detect_static_bodies, with the recommendation that you set up your collision layers/masks in such a way that only the relevantArea3Dare able to detect collisions with static bodies.This should probably be made into an
Area3Dproperty instead, through something like what's been suggested here.Joint properties
[Click to expand/collapse]
The current interfaces for the 3D joint nodes, which seem to be derived from the Bullet Physics library, don't quite line up with the interface of Jolt's own joints. As such, there are a number of joint properties that are not supported, mainly ones related to configuring the joint's soft limits.
The unsupported properties are:
PinJoint3D:bias,damping,impulse_clampHingeJoint3D:bias,softness,relaxationSliderJoint3D:angular_*,*_limit/softness,*_limit/restitution,*_limit/dampingConeTwistJoint3D:bias,relaxation,softnessGeneric6DOFJoint3D:*_limit_*/softness,*_limit_*/restitution,*_limit_*/damping,*_limit_*/erpCurrently an error is emitted if you set these properties to anything but their default values.
In the Godot Jolt extension I exposed alternative joint nodes that better matched Jolt's interface, but these have not been included in this module. Instead, the above mentioned properties should probably be removed and replaced by properties more fitting for Jolt, through something like what's been suggested here.
Single-body joints
[Click to expand/collapse]
You can, in Godot, omit one of the joint bodies for a two-body joint and effectively have "the world" be the other body. However, the node path that you assign your body to (
node_avsnode_b) is ignored. Godot Physics will always behave as if you assigned it tonode_a, and sincenode_ais also what defines the frame of reference for the joint limits, you end up with inverted limits and a potentially strange limit shape, especially if your limits allow both linear and angular degrees of freedom.This is arguably a bug and should be changed. This is also not how single-body joints behaves with Bullet in Godot 3, which behaves as if you assigned the body to
node_binstead. It's not clear why this changed in Godot 4.For this reason this module will, like Bullet in Godot 3, behave as if you assigned the body to
node_binstead, withnode_arepresenting "the world".There is a project setting called
physics/jolt_physics_3d/joints/world_nodethat lets you toggle this behavior, if you need compatibility for an existing project.Collision margins
[Click to expand/collapse]
Jolt (and other similar physics engines) uses something that Jolt refers to as "convex radius" to help improve the performance and behavior of the types of collision detection that Jolt relies on for convex shapes. Other physics engines (Godot included) might refer to these as "collision margins" instead. Godot exposes these as the
marginproperty on everyShape3D-derived class, as a leftover from the Bullet integration in Godot 3, but Godot Physics itself does not use them for anything.What these collision margins sometimes do in other engines (as described in Godot's documentation) is effectively add a "shell" around the shape, slightly increasing its size while also rounding off any edges/corners. In Jolt however, these margins are first used to shrink the shape, and then the "shell" is applied, resulting in edges/corners being similarly rounded off, but without increasing the size of the shape.
To prevent having to tweak this
marginproperty manually, since its default value can be problematic for smaller shapes, this module exposes a project setting calledphysics/jolt_physics_3d/collisions/collision_margin_fractionwhich is multiplied with the smallest axis of the shape's AABB to calculate the actual margin. Themarginproperty of the shape is then instead used as an upper bound.These margins should, for most use-cases, be more or less transparent, but can sometimes result in odd collision normals when performing shape queries. You can lower the above mentioned project setting to mitigate some of this, including setting it to
0, but too small of a margin can also cause odd collision results, so is generally not recommended.Compound shapes
[Click to expand/collapse]
Compound shapes (i.e. bodies with more than one shape) are implemented differently from Godot Physics.
Jolt offers the choice of two compound shapes, one called
MutableCompoundShapeand one calledStaticCompoundShape. The former trades in runtime performance for faster construction/modification time, and vice versa for the latter.Godot Physics maps closer to Jolt's
MutableCompoundShape, but I decided to go withStaticCompoundShapefor this implementation, as it simplified things a bit (with being able to discard and rebuild the whole thing when modified) and I figured more people would benefit from the improved runtime performance as opposed to mutation performance.To mitigate the cost of this potentially expensive rebuild, I made it so that shape changes are only ever "committed" to Jolt when the body has entered into a scene tree. This means that you can make shape changes (including adding/removing shapes) very quickly so long as the body isn't attached to the scene tree. However, if you do in fact add the body to a scene tree, and then start adding/removing/changing shapes on the body, you can end up with worse performance than Godot Physics. This is prominently visible in the "Voxel Game" demo project, as one example.
The plan I have in mind for this (discussed in jrouwe/JoltPhysics#1165) is to replace
StaticCompoundShapewith a custom compound shape that wrapsStaticCompoundShape, but which will always defer its building/committing only until absolutely necessary, meaning either right before a simulation step or when performing queries against the body.If the use of
StaticCompoundShapewithin this new custom compound shape still ends up being a problem for some use-cases it would likely be trivial to expose some setting or property that lets you useMutableCompoundShapeinstead.Scaling shapes/bodies/queries
[Click to expand/collapse]
Godot Physics supports scaling the transform of collision shapes, shape queries, as well as static and kinematic bodies, meaning
StaticBody3D,CharacterBody3D,AnimatableBody3Dand frozenRigidBody3D. It does not however support scaling simulated/dynamic bodies, such as a non-frozenRigidBody3D, and will effectively discard any such scaling, instead treating it as(1, 1, 1).Jolt does however support scaling everywhere, and I've tried my best to utilize that, which means that
RigidBody3Dwill support scaling when using Jolt, despite the warning shown on the node currently.Jolt also supports non-uniform scaling, so long as the inherent primitive shape is preserved. For example, you can scale a cylinder along its height axis but not along its other axes. You will however currently see warnings on the shape node when doing this, due to Godot Physics not supporting this.
Since invalid scaling can cause a number of weird artifacts, and sometimes outright crash the simulation, there are runtime error checks that "sanitize" all scaling to be valid for that particular shape arrangement, and then reports an error if the difference is above an arbitrary threshold. These errors have however proven to be a bit frustrating for some users, who might not care about the sometimes minor corrections that this error-checking applies, so this likely needs a different approach.
Shape-casting
[Click to expand/collapse]
Due to Godot having a "safe" and "unsafe" fraction in the results of
cast_motion(andShapeCast3D), meaning the distances at which the cast shape was found to be colliding and not colliding respectively, it was not viable to rely on Jolt's own shape-casting (which uses conservative advancement) to implementcast_motion. Insteadcast_motionis implemented with a binary search, similar to how Godot Physics does it.However, in Godot Physics this binary search is hardcoded to 8 steps, which tends to result in quite jittery output over even moderate distances. With Godot Jolt (and consequently this module) I chose to instead dynamically calculate the number of steps based on the cast distance, aiming for roughly millimeter precision, and then clamp it between 4 and 16 steps, which seems to have worked out well.
This does however mean that
cast_motionwill technically, when using Jolt, cost more CPU performance the farther you cast the shape.Note that this also applies to
body_test_motion, and consequentlytest_move,move_and_collideandmove_and_slide.Baumgarte stabilization
[Click to expand/collapse]
Jolt employs a technique in its solver called Baumgarte stabilization, which is meant to mitigate constraint drift within the simulation, resulting in a more stable simulation. This technique can however result in some artifacts, like piles of bodies not separating as quickly as one might expect.
The strength of this stabilization can be tweaked using the project setting
physics/jolt_physics_3d/simulation/baumgarte_stabilization_factor. Setting this project setting to1.0willeffectively disable the techniqueresolve any position error (e.g. penetration) in a single simulation step. Doing so will however result in a simulation that is more unstable.EDIT: It was pointed out that Godot Physics also utilizes this technique, but differs from Jolt in that it also updates the velocities after applying the Baumgarte stabilization. Not updating the velocities help with preventing overshoot, but is also what results in a slower error correction.
Motion queries (
move_and_slide, etc.)[Click to expand/collapse]
Physics servers in Godot are meant to implement the
PhysicsServer3D.body_test_motionmethod, which in turn powers methods likePhysicsBody3D.test_move,PhysicsBody3D.move_and_collideandCharacterBody3D.move_and_slide, which are largely meant to be used for moving player characters around.PhysicsServer3D.body_test_motionis split into three parts, the first being depenetration (called "recovery" in Godot), the second being a shape-cast from the "recovered" position, and the third being the actual collision check, at the "unsafe" fraction of the shape-cast.The "recovery" step in Godot Physics is hardcoded to always do 4 iterations with 40% depenetration per iteration. I figured these constants might be useful to expose, so when using this module they can be configured in the project settings as
physics/jolt_physics_3d/motion_queries/recovery_iterationsandphysics/jolt_physics_3d/motion_queries/recovery_amountrespectively.The implementation of
PhysicsServer3D.body_test_motionin this module also differs slightly from the Godot Physics implementation, as replicating the Godot Physics version resulted in a prohibitive amount of ghost collisions formove_and_slide.The discrepancies are as follows:
TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR.While these discrepancies do seem to result in less ghost collisions when using Jolt, they also introduce new problems:
move_and_slideis generally slower than in Godot Physics, due toCharacterBody3D::_snap_on_floortriggering on every call.CharacterBody3Dwill not report wall collisions unless you're moving towards them.It's not clear to me how to fix these issues, but I suspect they will require changes to
move_and_slideon the scene side of things.Ghost collisions
[Click to expand/collapse]
Jolt employs two techniques to mitigate ghost collisions, meaning collisions with internal edges of shapes/bodies.
The first technique, called "active edge detection", marks edges of triangles in
ConcavePolygonShape3DorHeightMapShape3Das either "active" or "inactive", based on the angle to the neighboring triangle. When a collision happens with an inactive edge the collision normal will be replaced with the triangle's normal instead, to lessen the effect of ghost collisions.The angle threshold for this active edge detection is configurable through the project setting
physics/jolt_physics_3d/collisions/active_edge_threshold.The second technique, called "enhanced internal edge removal", instead adds runtime checks to detect whether an edge is active or inactive, based on the contact points of the two bodies. This has the benefit of applying not only to collisions with
ConcavePolygonShape3DandHeightMapShape3D, but also edges between any shapes within the same body.Enhanced internal edge removal can be toggled on and off for the various contexts to which it's applied, using the
physics/jolt_physics_3d/*/use_enhanced_internal_edge_removalproject settings.Memory usage
[Click to expand/collapse]
Jolt uses a stack allocator for temporary allocations within its simulation step. This stack allocator requires allocating a set amount of memory up front, which can be configured using the
physics/jolt_physics_3d/limits/temporary_memory_buffer_sizeproject setting.Jolt also makes use of aligned allocations for some of its memory usage, which it acquires through function pointers that the implementer assigns. Aligned allocations were recently added to Godot in the form of
Memory::alloc_aligned_static,Memory::realloc_aligned_staticandMemory::free_aligned_static, which have all been hooked up to Jolt. However, these aligned allocation functions don't currently touchMemory::mem_usage, which means that some of Jolt's memory won't be tracked, and thus the performance monitors in Godot won't be accurate.Ray-cast face index
[Click to expand/collapse]
The
face_indexproperty returned in the results ofintersect_rayandRayCast3Dwill by default always be-1with Jolt, with a project setting (physics/jolt_physics_3d/queries/enable_ray_cast_face_index) to enable them. The reason for this being that Jolt does not store these indices, and for them to be stored we need to enable the per-triangle userdata, which adds about 25% extra memory usage to the underlying Jolt implementation ofConcavePolygonShape3D.This should maybe be made into a
ConcavePolygonShape3Dproperty instead, through something like what's been suggested here.Kinematic
RigidBody3Dcontacts[Click to expand/collapse]
When using Jolt, a
RigidBody3Dfrozen withFREEZE_MODE_KINEMATICwill by default not report contacts from collisions with other static/kinematic bodies, for performance reasons, even when setting a non-zeromax_contacts_reported. If you have many/large kinematic bodies overlapping with complex static geometry, such asConcavePolygonShape3DorHeightMapShape3D, you can end up wasting a significant amount of CPU performance without realizing it.For this reason this behavior is opt-in through the project setting
physics/jolt_physics_3d/simulation/generate_all_kinematic_contacts.This should probably be made into an
RigidBody3Dproperty instead, through something like what's been suggested here.Contact impulses
[Click to expand/collapse]
Jolt is not able to provide any impulse as part of its contact data, due to how it orders its simulation step, and instead provides a helper function for estimating what the impulse would be based on various parameters. While this is probably fine for most use cases, like emitting a sound based on how hard something collided, it won't be accurate if a body is colliding with multiple bodies during a simulation step.
Area3DandSoftBody3D[Click to expand/collapse]
This module does not currently support any interactions between
SoftBody3DandArea3D, such as overlap events, or the wind properties found onArea3D. Support for this has been added to Jolt recently, and @jrouwe also did some work towards the extension part of it, but it has not been included in this pull request.ConvexPolygonShape3D[Click to expand/collapse]
Godot Physics currently skips calculating a proper center-of-mass and inertia for
ConvexPolygonShape3D, and instead always uses the shape's local position as its center of mass, while crudely estimating the inertia. Jolt on the other hand does calculate a more accurate center-of-mass and inertia for them. As a result a non-frozenRigidBody3Dwith such a shape in it can behave differently when comparing the two engines.WorldBoundaryShape3D[Click to expand/collapse]
WorldBoundaryShape3D, which is meant to represent an infinite plane, is implemented a bit differently in Jolt compared to Godot Physics. Both engines have an upper limit for how big the effective size of this plane can be, but this size is much smaller when using Jolt, in order to avoid precision issues.You can configure this size using the
physics/jolt_physics_3d/limits/world_boundary_shape_sizeproject setting.Axis-locking
[Click to expand/collapse]
The
PhysicsBody3D.axis_lock_*properties in Godot Physics are implemented by simply zeroing out the velocities for the selected axes. Jolt instead implements these by calculating a new inverse mass/inertia, similar to howRigidBody3D.lock_rotationworks in Godot Physics, which seems to better mitigate energy loss during simulation.However, Jolt does not allow locking all axes, meaning both linear and angular axes, and an error will be emitted from this module when trying to do so, with the recommendation that you instead freeze the body entirely. While this is a simple enough workaround for
RigidBody3D, you cannot currently freeze aPhysicalBone3D, so if you want to lock all axes of aPhysicalBone3Dyou're forced to resort to callingPhysicsServer3D.body_set_modeyourself.Notable differences to Godot Jolt
While this module is largely a straight port of the Godot Jolt extension, with a lot of cosmetic changes, there are a few things that are different.
Project settings
[Click to expand/collapse]
All project settings have been moved from the
physics/jolt_3dcategory tophysics/jolt_physics_3d.On top of that, there's been some renaming and refactoring of the individual project settings as well. These include:
sleep/enabledis nowsimulation/allow_sleep.sleep/velocity_thresholdis nowsimulation/sleep_velocity_threshold.sleep/time_thresholdis nowsimulation/sleep_time_threshold.collisions/use_shape_marginsis nowcollisions/collision_margin_fraction, where a value of 0 is equivalent to disabling it.collisions/use_enhanced_internal_edge_removalis nowsimulation/use_enhanced_internal_edge_removal.collisions/areas_detect_static_bodiesis nowsimulation/areas_detect_static_bodies.collisions/report_all_kinematic_contactsis nowsimulation/generate_all_kinematic_contacts.collisions/soft_body_point_marginis nowsimulation/soft_body_point_radius.collisions/body_pair_cache_enabledis nowsimulation/body_pair_contact_cache_enabled.collisions/body_pair_cache_distance_thresholdis nowsimulation/body_pair_contact_cache_distance_threshold.collisions/body_pair_cache_angle_thresholdis nowsimulation/body_pair_contact_cache_angle_threshold.continuous_cd/movement_thresholdis nowsimulation/continuous_cd_movement_threshold, but expressed as a fraction instead of a percentage.continuous_cd/max_penetrationis nowsimulation/continuous_cd_max_penetration, but expressed as a fraction instead of a percentage.kinematics/use_enhanced_internal_edge_removalis nowmotion_queries/use_enhanced_internal_edge_removal.kinematics/recovery_iterationsis nowmotion_queries/recovery_iterations, but expressed as a fraction instead of a percentage.kinematics/recovery_amountis nowmotion_queries/recovery_amount.queries/use_legacy_ray_castinghas been removed.solver/position_iterationsis nowsimulation/position_steps.solver/velocity_iterationsis nowsimulation/velocity_steps.solver/position_correctionis nowsimulation/baumgarte_stabilization_factor, but expressed as a fraction instead of a percentage.solver/active_edge_thresholdis nowcollisions/active_edge_threshold.solver/bounce_velocity_thresholdis nowsimulation/bounce_velocity_threshold.solver/contact_speculative_distanceis nowsimulation/speculative_contact_distance.solver/contact_allowed_penetrationis nowsimulation/penetration_slop.limits/max_angular_velocityis now stored as radians instead.limits/max_temporary_memoryis nowlimits/temporary_memory_buffer_size.There might be some discussion to be had with regards to migrating the settings values for projects who have previously been relying on the extension.
Joint nodes
[Click to expand/collapse]
The joint nodes that are exposed in the Godot Jolt extension (
JoltPinJoint3D,JoltHingeJoint3D,JoltSliderJoint3D,JoltConeTwistJoint3DandJoltGeneric6DOFJoint) have not been included with this module.Instead of exposing bespoke joint nodes, the existing joint node interfaces should be modified by the physics server, through something like what's been suggested here.
Thread-safety
[Click to expand/collapse]
Unlike the Godot Jolt extension, this module does have experimental thread-safety, including support for the
physics/3d/run_on_separate_threadproject setting. This is achieved by utilizing the same wrapper server that's used by Godot Physics, calledPhysicsServer3DWrapMT, as well as introducing a mutex around the rebuilding of shapes, since concurrent shape-casts could otherwise trigger a race condition.This has however not been tested very thoroughly, so should be considered experimental.
Query performance
[Click to expand/collapse]
The Godot Jolt extension utilizes a custom container (typically referred to as an "inline vector" or "small vector") in order to avoid heap allocations for physics queries that return more than a single hit, meaning
intersect_point,intersect_shape,collide_shape,get_rest_info,cast_motion,body_test_motion,test_move,move_and_collideandmove_and_slide.For the sake of simplifying this port I chose to just use
JPH::Arrayfor the query collectors instead, which means that these queries will now always allocate on the heap, likely making them noticeably slower.It should be trivial to make some bespoke container for the query collectors that behaves like an inline/small vector.
Debug renderer
[Click to expand/collapse]
Jolt provides an interface called
JPH::DebugRendererfor rendering its view of the physics simulation. In the Godot Jolt extension I expose this as a customGeometryInstance3Dnode calledJoltDebugGeometry3D, which proved to be very useful during development, but I figured this module should probably take a different approach, so I have omitted that code.Debug snapshots
[Click to expand/collapse]
The Godot Jolt extension has the ability to generate what Jolt refers to as "snapshots", where it serializes the state of the physics simulation to a file. These can then be loaded in Jolt's own
Samplesapplication, to debug issues more closely there, without needing to deal with Godot itself, which has proved to be quite useful when reporting issues upstream to Jolt.The code for this is technically included with this module, and can be found as
JoltPhysicsServer3D::dump_debug_snapshots, but it wasn't clear to me how to best expose this, so I've left it unexposed for now.Things left to do
Just to give people an idea about the state of this module, and perhaps encourage future contributions if/when this pull request is merged, here is a rough copy of my to-do list:
Must-have
Area3DandSoftBody3D.PhysicalBone3D.Nice-to-have
Get rid of heap allocations in shape queries when requested hits are less or equal to default.dev_build.Get rid of the body accessors injolt_body_accessor_3d.h, in favor of just storing theJPH::Body*inJoltObject3D.Try remove the use ofJPH::PhysicsSystem::GetBodies, to avoid the overhead of iterating over static/sleeping bodies.Jolt.natvisfile in MSVC builds for easier debugging.What about the extension?
If/when this pull request is merged, the Godot Jolt extension will officially be considered to be in maintenance mode going forward, with only bug fixes backported to it, but so long as this engine module doesn't have full feature parity with the extension there will be new releases of the extension to ensure that projects relying on its additional features can continue to function.
If/when this engine module reaches full feature parity with the extension then the extension will be discontinued and its GitHub repository archived.
Attribution
In the interest of not cluttering every file in this new module with the copyright notice of Godot Jolt, as required by its MIT license, consider this my permission to omit it entirely. I have obtained permission from the applicable copyright holders (@jrouwe) to do so as well.
(This port is based on godot-jolt/godot-jolt@8f1212e, meaning the latest commit of Godot Jolt as of writing this.)