Describe the bug
The e:pos() function returns an interpolation of the m_world_last and m_world_next matrices, but the velocity is applied to an object relatively to the m_world_next matrix. So, currently we can't move an object directly relative to the result of the e:pos() function.
I implemented a destructive way of calculating the m_world_last matrix using Expression 2. To get m_world_next, just apply e:vel() and e:angVelVector() to it. It's destructive, because it changes the angular velocity of the object to rip the matrix.
Instead of calculating a m_world_last matrix, you can use the current physics tick factor, multiply it by the velocity and add the interpolated matrix. The way of finding the current physics tick begin and end time described in this issue: #3575.
On the Lua side the m_world_last matrix can be calculated using PhysObj:CalculateForceOffset() function in a non-destructive way. It works only if all rotation matrix elements are greater than 2^(-84), so 2^122 * m11 + 16384 * m12 == 2^127 * m11. All bits of the result beyond this range have to be truncated to prevent corruption. The PhysObj:CalculateForceOffset() function just calls CPhysicsObject::CalculateForceOffset() from the SDK.
local meter = 39.370079040527344 -- in
local radian = 57.295780181884766 -- deg; 180 / M_PI_F
local big_float = 2^122
local phys = Entity(76):GetPhysicsObject()
local _, r1 = phys:CalculateForceOffset(Vector(0, 0, meter), Vector(0, big_float * meter, 0))
local _, r2 = phys:CalculateForceOffset(Vector(meter, 0, 0), Vector(0, 0, big_float * meter))
local _, r3 = phys:CalculateForceOffset(Vector(0, meter, 0), Vector(big_float * meter, 0, 0))
r1 = r1 / radian / big_float
r2 = r2 / radian / big_float
r3 = r3 / radian / big_float
local m = Matrix({{r1[1], r1[2], r1[3], 0}, {r2[1], r2[2], r2[3], 0}, {r3[1], r3[2], r3[3], 0}, {0, 0, 0, 1}})
local _, t12 = phys:CalculateForceOffset(Vector(0, 0, meter), Vector(0, 0, 0))
local _, t3 = phys:CalculateForceOffset(Vector(0, meter, 0), Vector(0, 0, 0))
t12 = m * (t12 / radian)
t3 = m * (t3 / radian)
m:SetTranslation(Vector(t12[2], -t12[1], t3[1]) * meter)
print(m)
I'm not sure how to solve this on the Wiremod side. There are many functions using the interpolated matrix: e:toWorld(v), e:pos(), e:angles(), e:forward(), matrix(e), and probably everything related to get position and angle. On the other hand, all functions related to object movement use the m_world_next matrix: e:applyForce(v), e:propSetVelocity(v) and the gravity. Some specific functions use m_world_last: e:velAtPoint(v), e:applyOffsetForce(vv), e:applyAngForce(a). The e:applyTorque(v) function used to use m_world_last, but I recently patched it to use m_world_next: #3560. We need a way to convert values between any functions to get rid of mismatches.
Currently I don't see any non-destructive way to direct access the m_world_last or m_world_next matrix from Expression 2. My current implementation calls e:applyTorque(v) twice per a matrix row extraction. Also, the values obtained using this method are dirty due to precision loss during conversion torque to angular velocity.
How to reproduce the bug
Run game with -tickrate 33. Spawn a cube025x025x025 and place the sample chip.
test_world_matrix.txt
TestType = 0. On the first game tick this sample sets the velocity which should move a prop by 1 in by the next game tick. On the second game tick it prints the current position 0.9 in and sets the velocity to zero, so the prop should stay on its current place. But, when the third tick prints the current position, we see that it's exactly 1 in. For the object to actually stay in place, we should think of its position as 1 in and calculate the velocity required to move it from that point to 0.9 in, instead of setting the velocity to zero.
TestType = 1. An implementation of a destructive way to find m_world_last. It also calculates m_world_next by add the object velocity to m_world_last. It prints the actual object position compared to the e:pos(): 0.5 in as the last pos and 1 in as the next pos, instead of an interpolated 0.9 in pos. As a side effect it sets the velocity to [0 0 0].
TestType = 2. A non-destructive way to calculate both matrices relatively to the interpolated matrix using game and physics tick sync. It requires to find GameTickOffset first, a way is described in the issue mentioned above. It calculates the factor F used by IVP_Core::calc_at_matrix() function and calculates m_world_last by undo it. Then it just applies the object velocity to m_world_last to get m_world_next, this is the same as if the factor is 1. It doesn't affect the object velocity, so it keeps moving up and rotating around Z. Currently this is the best way to access these matrices I know.
Describe the bug
The
e:pos()function returns an interpolation of them_world_lastandm_world_nextmatrices, but the velocity is applied to an object relatively to them_world_nextmatrix. So, currently we can't move an object directly relative to the result of thee:pos()function.I implemented a destructive way of calculating the
m_world_lastmatrix using Expression 2. To getm_world_next, just applye:vel()ande:angVelVector()to it. It's destructive, because it changes the angular velocity of the object to rip the matrix.Instead of calculating a
m_world_lastmatrix, you can use the current physics tick factor, multiply it by the velocity and add the interpolated matrix. The way of finding the current physics tick begin and end time described in this issue: #3575.On the Lua side the
m_world_lastmatrix can be calculated usingPhysObj:CalculateForceOffset()function in a non-destructive way. It works only if all rotation matrix elements are greater than2^(-84), so2^122 * m11 + 16384 * m12 == 2^127 * m11. All bits of the result beyond this range have to be truncated to prevent corruption. ThePhysObj:CalculateForceOffset()function just callsCPhysicsObject::CalculateForceOffset()from the SDK.I'm not sure how to solve this on the Wiremod side. There are many functions using the interpolated matrix:
e:toWorld(v),e:pos(),e:angles(),e:forward(),matrix(e), and probably everything related to get position and angle. On the other hand, all functions related to object movement use them_world_nextmatrix:e:applyForce(v),e:propSetVelocity(v)and the gravity. Some specific functions usem_world_last:e:velAtPoint(v),e:applyOffsetForce(vv),e:applyAngForce(a). Thee:applyTorque(v)function used to usem_world_last, but I recently patched it to usem_world_next: #3560. We need a way to convert values between any functions to get rid of mismatches.Currently I don't see any non-destructive way to direct access the
m_world_lastorm_world_nextmatrix from Expression 2. My current implementation callse:applyTorque(v)twice per a matrix row extraction. Also, the values obtained using this method are dirty due to precision loss during conversion torque to angular velocity.How to reproduce the bug
Run game with
-tickrate 33. Spawn a cube025x025x025 and place the sample chip.test_world_matrix.txt
TestType = 0. On the first game tick this sample sets the velocity which should move a prop by 1 in by the next game tick. On the second game tick it prints the current position 0.9 in and sets the velocity to zero, so the prop should stay on its current place. But, when the third tick prints the current position, we see that it's exactly 1 in. For the object to actually stay in place, we should think of its position as 1 in and calculate the velocity required to move it from that point to 0.9 in, instead of setting the velocity to zero.TestType = 1. An implementation of a destructive way to findm_world_last. It also calculatesm_world_nextby add the object velocity tom_world_last. It prints the actual object position compared to thee:pos(): 0.5 in as the last pos and 1 in as the next pos, instead of an interpolated 0.9 in pos. As a side effect it sets the velocity to [0 0 0].TestType = 2. A non-destructive way to calculate both matrices relatively to the interpolated matrix using game and physics tick sync. It requires to findGameTickOffsetfirst, a way is described in the issue mentioned above. It calculates the factor F used byIVP_Core::calc_at_matrix()function and calculatesm_world_lastby undo it. Then it just applies the object velocity tom_world_lastto getm_world_next, this is the same as if the factor is 1. It doesn't affect the object velocity, so it keeps moving up and rotating around Z. Currently this is the best way to access these matrices I know.