[BUG 3] Simultaneity Breaks Physics #16

Open
opened 2025-08-30 02:12:21 +00:00 by Quaternions · 4 comments
Owner

I've root caused why you fall through the trigger at the end of bhop_toc. It's not because of the alignment, it's because two physics events are happening simultaneously, and the physics system can't handle zero-time events correctly.

See: bug3-real branch

This section of code prevents zero-time events:

//temp (?) code to avoid collision loops
.and_then(|(face,dt)|{
// this must be rounded to avoid the infinite loop when hitting the start zone
let time=relative_body.time+dt.into();
(state.time<time).then_some((time,face,dt))

TODO: remove and_then, but recreate it as an if statement and put a breakpoint inside to debug why it happens

I've root caused why you fall through the trigger at the end of bhop_toc. It's not because of the alignment, it's because two physics events are happening simultaneously, and the physics system can't handle zero-time events correctly. See: [`bug3-real` branch](https://git.itzana.me/StrafesNET/strafe-project/src/branch/bug3-real) This section of code prevents zero-time events: https://git.itzana.me/StrafesNET/strafe-project/src/commit/3692d7f79e094a4fcd64203843700e1f2755fb77/engine/physics/src/physics.rs#L1169-L1173 TODO: remove and_then, but recreate it as an if statement and put a breakpoint inside to debug why it happens
Member

I've been doing some testing, and hopefully some of these details and questions might help
This is specifically focusing on the implementation of the "bug_3" test as seen on the bug3-real branch (with 9914f6b removed to test as if it was the master branch cause I've got no clue what that commit is doing)

On the second iteration (we've collided with the first mesh, expecting to collide with the second mesh), the logic will initially find the collision ending with the first mesh at 2s, and then fail to find a starting collision within the new time bounds, since the new bounds are start <= x < end, and the end is now 2s, which is the exact collision start time we're looking for, so it's considered out of range
Even if the collision start event was found by expanding to an ..= range and changing the aabb to behave like a <=x<= range instead of a <=x< range (god knows what implications that'd have), the collector would disregard it as it's specifically looking for an event that is LT the current best event instead of LE, so the collision end event wins

Then, on the third iteration, the collision start isn't found because... well, not sure on that one, but I'm not sure if we should be even reaching that case

How much of the above is intended/expected behaviour? Also, the test expects nothing on the third iteration, so is the intended behaviour of this test either
A) the collision start is prioritised over the collision end for that tick, preventing the collision from ending,
B) the collision ends and then the collision start with the other mesh is found on the next tick but at the same time of 2s, or
C) both events are meant to take place in the same physics iteration (this one seems least likely)?

I've been doing some testing, and hopefully some of these details and questions might help This is specifically focusing on the implementation of the "bug_3" test as seen on the bug3-real branch (with 9914f6b removed to test as if it was the master branch cause I've got no clue what that commit is doing) On the second iteration (we've collided with the first mesh, expecting to collide with the second mesh), the logic will initially find the collision ending with the first mesh at 2s, and then fail to find a starting collision within the new time bounds, since the new bounds are `start <= x < end`, and the end is now 2s, which is the exact collision start time we're looking for, so it's considered out of range Even if the collision start event was found by expanding to an `..=` range and changing the aabb to behave like a `<=x<=` range instead of a `<=x<` range (god knows what implications that'd have), the collector would disregard it as it's specifically looking for an event that is LT the current best event instead of LE, so the collision end event wins Then, on the third iteration, the collision start isn't found because... well, not sure on that one, but I'm not sure if we should be even reaching that case How much of the above is intended/expected behaviour? Also, the test expects nothing on the third iteration, so is the intended behaviour of this test either A) the collision start is prioritised over the collision end for that tick, preventing the collision from ending, B) the collision ends and then the collision start with the other mesh is found on the next tick but at the same time of 2s, or C) both events are meant to take place in the same physics iteration (this one seems least likely)?
Author
Owner

How much of the above is intended/expected behaviour? Also, the test expects nothing on the third iteration, so is the intended behaviour of this test either

I think you've found two separate issues which will both individually cause the observed outcome, which is why I didn't see the issue get fixed when changing them individually. This is insightful and I will have to look into it when I sit down at my desktop at home ready to get shit done. You might actually be able to fix the bug by messing with the logic and seeing if you stop clipping through the geometry.

My intention is to end the first tick with either event completing, and then the second tick does not advance the clock, but handles the second simultaneous collision event. The hope is that by adding it to the contacts list it will not be considered again in a third tick. I think this is the best way to handle it because of how rare simultaneous collision events are. Two other options would be to have a state machine spanning multiple PhysicsInstructions to collect and then process events, or to stuff multiple events into a list input to one PhysicsInstruction.

> How much of the above is intended/expected behaviour? Also, the test expects nothing on the third iteration, so is the intended behaviour of this test either I think you've found two separate issues which will both individually cause the observed outcome, which is why I didn't see the issue get fixed when changing them individually. This is insightful and I will have to look into it when I sit down at my desktop at home ready to get shit done. You might actually be able to fix the bug by messing with the logic and seeing if you stop clipping through the geometry. My intention is to end the first tick with either event completing, and then the second tick does not advance the clock, but handles the second simultaneous collision event. The hope is that by adding it to the contacts list it will not be considered again in a third tick. I think this is the best way to handle it because of how rare simultaneous collision events are. Two other options would be to have a state machine spanning multiple PhysicsInstructions to collect and then process events, or to stuff multiple events into a list input to one PhysicsInstruction.
Member

Under the current implementation, the second event (the event which has two things happening at the same time - the collision start and the collision end) ends up being the collision end since that's checked for first, moving us to two seconds, and puts us at the exact time at which the collision start would be expected. However, on the next physics tick, the collision start is not picked up by predict_collision_in to be considered, as closest_fev_not_inside ends up landing a hit against the mesh (it expects a miss), meaning a collision is not considered and as such we never collide with the second mesh. Also, even if we did collide with the second mesh, since the collision would be taking place at dt=0, I presume the initial .and_then in the first post of this issue would probably reject the collision

No clue what exactly in the above steps could be considered "incorrect", but maybe it'll help in this written flow

Addendum: I just realised, why tf is all of next_instruction_internal tabbed one to the right even though nothing else around it is

Under the current implementation, the second event (the event which has two things happening at the same time - the collision start and the collision end) ends up being the collision end since that's checked for first, moving us to two seconds, and puts us at the exact time at which the collision start would be expected. However, on the next physics tick, the collision start is not picked up by `predict_collision_in` to be considered, as `closest_fev_not_inside` ends up landing a hit against the mesh (it expects a miss), meaning a collision is not considered and as such we never collide with the second mesh. Also, even if we did collide with the second mesh, since the collision would be taking place at dt=0, I presume the initial `.and_then` in the first post of this issue would probably reject the collision No clue what exactly in the above steps could be considered "incorrect", but maybe it'll help in this written flow Addendum: I just realised, why tf is all of `next_instruction_internal` tabbed one to the right even though nothing else around it is
Author
Owner

closest_fev_not_inside ends up landing a hit against the mesh (it expects a miss), meaning a collision is not considered and as such we never collide with the second mesh.

Space and time coverage needs to be carefully considered to cover ranges exactly once. Think about the integer coordinates like voxels. Let's say the voxel resolution is 100 voxels per unit (it's actually 2^32). Are voxels centered around their coordinate? Does the voxel at 0,0,0 extend by 0.005 units in positive and negative directions? Does a 5x5x5 part sitting at 0,0,0 include 500 voxels along the x axis or 501?

My plan is to try to make ranges non-inclusive. Voxels are not centered around their coordinate. The "0,0,0" voxel only extends 0.01 unit in the positive direction. A 5x5x5 part would have 500 voxels along an edge, not 501.

The problem is that the face crawling algorithm is using <= whether you're approaching in the negative axis direction or the positive axis direction, so objects are potentially one voxel larger when approaching in the negative direction, so 501 voxels. It will take very careful thinking to make everything work correctly.

Addendum: I just realised, why tf is all of next_instruction_internal tabbed one to the right even though nothing else around it is

It's because it used to exist in a deeper scope, I like to put off formatting and do it in one big messy commit (and I forgot about it).

> `closest_fev_not_inside` ends up landing a hit against the mesh (it expects a miss), meaning a collision is not considered and as such we never collide with the second mesh. Space and time coverage needs to be carefully considered to cover ranges exactly once. Think about the integer coordinates like voxels. Let's say the voxel resolution is 100 voxels per unit (it's actually 2^32). Are voxels centered around their coordinate? Does the voxel at 0,0,0 extend by 0.005 units in positive and negative directions? Does a 5x5x5 part sitting at 0,0,0 include 500 voxels along the x axis or 501? My plan is to try to make ranges non-inclusive. Voxels are not centered around their coordinate. The "0,0,0" voxel only extends 0.01 unit in the positive direction. A 5x5x5 part would have 500 voxels along an edge, not 501. The problem is that the face crawling algorithm is using <= whether you're approaching in the negative axis direction or the positive axis direction, so objects are potentially one voxel larger when approaching in the negative direction, so 501 voxels. It will take very careful thinking to make everything work correctly. > Addendum: I just realised, why tf is all of `next_instruction_internal` tabbed one to the right even though nothing else around it is It's because it used to exist in a deeper scope, I like to put off formatting and do it in one big messy commit (and I forgot about it).
Sign in to join this conversation.
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: StrafesNET/strafe-project#16