X Tutup
Skip to content

Fix SoftBody3D's position influences its physics in Jolt#112483

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
jrouwe:issue_112348
Nov 11, 2025
Merged

Fix SoftBody3D's position influences its physics in Jolt#112483
Repiteo merged 1 commit intogodotengine:masterfrom
jrouwe:issue_112348

Conversation

@jrouwe
Copy link
Contributor

@jrouwe jrouwe commented Nov 6, 2025

The position of a soft body was always kept at identity. This introduced computational errors when moving the soft body away from the origin. Translation is now stored in the soft body's position rather than in its vertices.

Fixes #112348

@jrouwe jrouwe requested a review from a team as a code owner November 6, 2025 19:52
@jrouwe
Copy link
Contributor Author

jrouwe commented Nov 6, 2025

@mihe, can you please look at this carefully. It solves the MRP that was provided, but I might have broken other things.

@mihe mihe added this to the 4.6 milestone Nov 7, 2025
@AThousandShips AThousandShips changed the title SoftBody3D's position influences its physics in Jolt Fix SoftBody3D's position influences its physics in Jolt Nov 7, 2025
@mihe
Copy link
Contributor

mihe commented Nov 7, 2025

It solves the MRP that was provided, but I might have broken other things.

@jrouwe It does indeed seem to break the re-adding of a SoftBody3D that has previously been removed from the scene tree, where it'll appear way off in the distance when added back into the scene tree. You can reproduce it in this modified version of the MRP found in #112348: soft-body-reentering-issue.zip

It also seems to trigger a Jolt assertion when the body is added back again:

ERROR: Jolt Physics assertion 'mat_eigvec.IsClose(eigval_eigvec, max(mat_eigvec.LengthSq(), eigval_eigvec.LengthSq()) * 1.0e-6f)' failed with message '' at 'thirdparty/jolt_physics/Jolt/Math/EigenValueSymmetric.h:88'

EDIT: It looks like the Jolt assertion happens even without this PR, so I guess that's something to address separately.

Apart from that, I'm not seeing any issues in the few projects I've tested with. Functionality that involve the vertex positions, like moving pinned vertices, seems to work fine.

The position of a soft body was always kept at identity. This introduced computational errors when moving the soft body away from the origin. Translation is now stored in the soft body's position rather than in its vertices.

Fixes godotengine#112348
@jrouwe
Copy link
Contributor Author

jrouwe commented Nov 7, 2025

I have not triggered any assert on my side.

The way the transforms are handled with soft bodies is 'interesting'. The problem is that in JoltSoftBody3D::_ref_shared_data we're getting the vertices from the mesh. When it is first inserted, these vertices are in local space. When the body is inserted the 2nd time, they are in world space, presumably because of:

// 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.

I'm not sure if that means that JoltSoftBody3D::_ref_shared_data has been incorrect all along as it uses a global map to map from mesh RID to shared mesh settings. This may mean that if soft bodies A and B share the same mesh:

  • insert A, remove A (this removes the shared mesh as it is was last ref), insert A -> mesh is in world space.
  • insert A, insert B, remove B, insert B -> mesh from original A insert is used which is in local space -> failure (both with the old code and the new).

@mihe
Copy link
Contributor

mihe commented Nov 8, 2025

I have not triggered any assert on my side.

Are you using dev_build=yes? You'll need that for Jolt assertions to be enabled. I'm still seeing the assertion in the project I linked above.

The way the transforms are handled with soft bodies is 'interesting'.

Yeah, SoftBody3D as a node has never been in a good state. It needs a serious overhaul in general.

I'm not sure if that means that JoltSoftBody3D::_ref_shared_data has been incorrect all along

Well, I'm not sure how I missed this in my initial implementation, but it turns out this whole sharing of JPH::SoftBodySharedSettings is moot during actual runtime/simulation anyway, since SoftBody3D will always copy its mesh in non-editor runs in order to add/remove some flags on the mesh:

RID mesh_rid = mesh->get_rid();
if (owned_mesh != mesh_rid) {
_become_mesh_owner();
mesh_rid = mesh->get_rid();
}
PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid);

surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
surface_format &= ~Mesh::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
Ref<ArrayMesh> soft_mesh;
soft_mesh.instantiate();
soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_lods, surface_format);
soft_mesh->surface_set_material(0, mesh->surface_get_material(0));
set_mesh(soft_mesh);

It's only in the editor (for some reason) that the meshes are left as-is and thus shared, which I guess I must have extrapolated to think that it applied to runtime as well, without actually double-checking to see if that was the case.

I guess there's reasonable justification for just removing all that stuff then, which would not only simplify the code a bit, but also mean that JoltSoftBody3D::set_stiffness_coefficient could actually update its JPH::SoftBodySharedSettings::VertexAttributes::mCompliance when changed.

@mihe mihe added the cherrypick:4.5 Considered for cherry-picking into a future 4.5.x release label Nov 8, 2025
@jrouwe
Copy link
Contributor Author

jrouwe commented Nov 8, 2025

Are you using dev_build=yes? You'll need that for Jolt assertions to be enabled. I'm still seeing the assertion in the project I linked above.

I was somehow assuming that an assert would trigger a breakpoint in the debugger, but I see it isn't. I'm getting the assert too.

The issue is that on reinsert, the vertices are all in world space. This causes some precision issues in the inertia tensor calculation and triggers this assert. I don't think it's a big deal as the error is relatively small and the inertia isn't used for anything anyway:

image

I would say we merge this change to fix the bug and then we remove the sharing of the mesh in another change. When we do that, we can get rid of:

jolt_settings->mPosition = JPH::RVec3::sZero(); // We'll be getting the vertices in world space when we re-insert the body so we need to reset the position here.

and when extracting the mesh vertices, subtract jolt_settings->mPosition so that the inertia calculation is local again. This also has the benefit that the sim won't see world space vertices on reinsert during the first update (before it recenters due to jolt_settings->mUpdatePosition = true;).

Do you want to remove the sharing code or should I do it?

@mihe
Copy link
Contributor

mihe commented Nov 8, 2025

Do you want to remove the sharing code or should I do it?

You're more than welcome to.

jrouwe added a commit to jrouwe/godot that referenced this pull request Nov 10, 2025
Since the soft body meshes are always copied before simulation, the shared map never actually shared data with anything. This also makes it possible to create the mesh in local space the 2nd time it gets added to a space. Fixes an assert in inertia calculation and a 1 frame inaccuracy in the pressure calculation.

See godotengine#112483 (comment)
jrouwe added a commit to jrouwe/godot that referenced this pull request Nov 10, 2025
Since the soft body meshes are always copied before simulation, the shared map never actually shared data with anything. This also makes it possible to create the mesh in local space the 2nd time it gets added to a space. Fixes an assert in inertia calculation and a 1 frame inaccuracy in the pressure calculation.

See godotengine#112483 (comment)
@Repiteo Repiteo merged commit a9b8f92 into godotengine:master Nov 11, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Nov 11, 2025

Thanks!

@akien-mga
Copy link
Member

Cherry-picked for 4.5.2.

@akien-mga akien-mga removed the cherrypick:4.5 Considered for cherry-picking into a future 4.5.x release label Jan 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SoftBody3D's Position Influences its Physics in Jolt

4 participants

X Tutup