Rigidbody on top of Staticbody becoming NaN after being landed on by another rigidbody (seemingly only when coordinates include 0)

Godot Version

4.4.1

Question

I have a rigidbody that is instantiated at runtime based on user action. When at the initial location of the player spawn (0, 1.5, 0), aiming down, and placing a pot after jumping, once landing, the position of both the pot and player become NaN. I want to use a RigidBody player controller because the player should be able to interact with objects. I am using Jolt physics, in case that could be a problem.

For testing purposes, the code is available here.

Question (old)

I made a thing to add an object to the scene when clicked, but it causes an error when any of the coordinates are zero. I assume it’s some divide by zero error, but I checked everything that seemed obvious to be a problem, but could not find out a fix. I checked the positions of everything, but it all seems to be reasonable. I also logged the transform matrix, which also looks fine.

Edit: also, the world disappears, but only sometimes, despite the same actions.
When the world doesn’t disappear, the collision with the item doesn’t work. Anywhere else, the collision does work fine.

Here’s the script that should be causing the bug.

You can post code here like so:

```gdscript
code
```

Your code:

extends Node

func place_item(caster: RayCast3D, item: String):
	for i in GlobalConstants.PARTS:
		if i.part_id == item:
			var rigid_parent: RigidBody3D
			
			if caster.get_collider().get_collision_layer_value(2):
				rigid_parent = RigidBody3D.new()
				rigid_parent.collision_layer = 0b101
				rigid_parent.can_sleep = false
				rigid_parent.mass = i.part_mass
				get_tree().get_first_node_in_group("scene root").add_child(rigid_parent)
			elif caster.get_collider().get_collision_layer_value(3):
				rigid_parent = caster.get_collider()
				rigid_parent.mass = rigid_parent.mass + i.part_mass
			
			var part = i.part_scene.instantiate()
			var offset = part.position
			rigid_parent.add_child(part)
			
			var point = caster.get_collision_point()
			var normal = caster.get_collision_normal().normalized()
			
			var new_transform = Transform3D(Basis(), offset)
			var normal_cross = Vector3.UP.cross(normal)
			
			if not normal_cross.is_zero_approx():
				new_transform = new_transform.rotated(normal_cross.normalized(), acos(Vector3.UP.dot(normal)))
			
			new_transform.origin = new_transform.origin + point
			
			part.global_transform = new_transform

The docs say get_collision_normal() returns a zero vector if the ray starts inside the thing it hits. You’re calling normalize() on it. Two things:

  • normalizing a normal is redundant
  • unless normalize() explicitly checks for zero vectors, it’s going to try to divide each component of the vector (all of which are 0) by the length, (which is 0), and hilarity will ensue

Thanks for the help, and I have now removes the normalized functions, however it still doesn’t seem to be working.

You still potentially need to deal with that zero vector; your normal var looks like it’s coming in zero sometimes, which means that (for example) Vector3.UP.cross(normal) is going to be trying to cross the up vector with a zero vec, which will make normal_cross be a zero vec.

Are you still seeing the is_finite error? If not, how is it not working?

I am still seeing the error. I did add a check to make sure that the normal is not zero, and am still getting the error.

Are you sure the error is in this script? Can you copy the error output here?

The entire error I am getting is this
E 0:00:01:798 instance_set_transform: Condition "!v.is_finite()" is true. <C++ Source> servers/rendering/renderer_scene_cull.cpp:957 @ instance_set_transform()

I can setup a repo with the full game so far, if you want more info.

Ok, well, at the least that tells you the problem is with a non-finite transform. That suggests a problem with new_transform. Are all of its components looking reasonable?

For that matter, how sure are you that the problem is in this code?

Well, I’m not sure, but the error only appears after the code is run. It might be some chain reaction. The transform looks fine:
[X: (1.0, 0.0, 0.0), Y: (0.0, 1.0, 0.0), Z: (0.0, 0.0, 1.0), O: (-0.0, 0.929514, 0.0)]

One thing of note. You’re setting global_transform, which means that to generate the local transform for part it’s going to have to math its way through all the node transforms between path and the root. So the problem could potentially be on anything above it in the hierarchy.

After a lot of fiddling and rewriting, I’m no longer able to reproduce the bug. So, I guess it’s solved, but I’m not sure how. Maybe it is because two rigidbodies intersecting? That wouldn’t explain the fact that the position of all the rigidbodies are normal, so I’m not sure.

Bump, because I’ve found out a lot more about the bug and it’s broken itself again.

Alright, I’ve managed a janky solution, that works. Basically, giving a script to all of the rigid bodies I have, it doesn’t break, but it’s also very hacky and in an ideal world wouldn’t be needed.

extends RigidBody3D

func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
	if is_zero_approx(state.transform.origin.x):
		state.transform.origin.x = 0.000001
	if is_zero_approx(state.transform.origin.y):
		state.transform.origin.y = 0.000001
	if is_zero_approx(state.transform.origin.z):
		state.transform.origin.z = 0.000001

Basically, this just makes it so that the rigid bodies never have a coordinate that is zero so the bug can’t happen. I still don’t know why it happens in the first place. Gotta be something to do with that 0. I wanna say it’s a bug with Jolt, but I’m nearly certain it’s not.