-
Notifications
You must be signed in to change notification settings - Fork 348
Description
Describe the bug
Currently E2 uses a world matrix from the current tick, but the applyTorqueCenter engine function expects an input to be in the world space of the previous tick.
This happens, because LocalToWorldVector calls GetPositionMatrix to get the world matrix. But that function uses get_m_world_f_object_AT which returns an interpolation between q_world_f_core_last_psi and q_world_f_core_next_psi. The interpolation factor is a time between the current tick and the next tick. At the time of executing event tick(), this time is close to the end of the tick, so the result is close to q_world_f_core_next_psi. The exact factor I measured in single-player is 0.999989986, but in multi-player this probably may be 0.5 or anything else.
On the other hand, the ApplyTorqueCenter function calls async_rot_push_core_multiple_ws which uses get_m_world_f_core_PSI to get m_world_f_core_last_psi which equals to q_world_f_core_last_psi and absolutely different from q_world_f_core_next_psi.
So, the function LocalToWorldVector have to be replaced with something which uses q_world_f_core_last_psi instead of interpolated matrix.
As a workaround, I undo this conversion by wrapping the torque input value with ent:toLocalAxis() and applying my own conversion multiplying it by a matrix saved from the previous tick at the end of the event OldWorldMatrix = matrix(entity).
How to reproduce the bug
Here is a sample chip. Join single-player, spawn cube025x025x025, freeze it a few units above the ground and place this chip at any place on the ground. The object orientation will match the world space and it must start spinning around the global Z axis.
test_m_world_last_next.txt
Uncomment the following line to view a bad behavior.
E:applyTorque(AV * E:inertia() / (180 / _PI / 39.3701^2))
Object will be spinning around local X (global X) axis. This is a bad behavior, because we are applying torque to spin it only around local Z axis which matches the global Z axis vec(0, 0, 1000).
Then comment it back and uncomment the following line to view a good behavior.
E:applyTorque(E:toLocalAxis(OldWorldMatrix * (AV * E:inertia()) / (180 / _PI / 39.3701^2)))
E:toLocalAxis(...) is used to undo phys:LocalToWorldVector(...) applied internally, because it uses q_world_f_core_next_psi which we don't want.
OldWorldMatrix * (...) is an attempt to access m_world_f_core_last_psi matrix. It works fine in single-player, but worse in multi-player, since LocalToWorld returns an interpolated matrix and not pure q_world_f_core_next_psi.
Object will be spinning around the global Z axis, as expected.