-
-
Notifications
You must be signed in to change notification settings - Fork 22.5k
Fix broken negative scaling when using Jolt Physics #103440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2533b0d
to
7e85cd5
Compare
7e85cd5
to
b125f2e
Compare
b125f2e
to
62e8b1e
Compare
In godot engine the common conversion from Basis to quaternion is get_rotation_quaternion. I don't know why we're doing it this way. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please explain why we can't use get_rotation_quaternion or orthogonalize?
https://github.com/godotengine/godot/blob/master/core/math/basis.cpp#L80
@fire Sorry, I wasn't very clear in the PR description, but as mentioned in the issue, in a lot of these cases we are not in fact converting our I must admit I'm not sure if the handedness is strictly important to resolve before passing a transform to Jolt though, but seeing as how the extracted (potentially negative) scale that goes along with it is what's meant to represent any reflection, the extraction of which seems to be some matter of convention (flipping one versus three axes) I assume we should be stripping said reflection from the transforms as well? Perhaps @jrouwe can shed some light on whether not doing so would cause any problems in Jolt, or trigger some assertion somewhere. Either way, As far as I can see, if we wanted to only make use of existing void decompose(Basis &p_basis, Vector3 &r_scale) {
// Extract the scale.
r_scale = p_basis.get_scale();
// Orthogonalize and normalize.
p_basis.orthonormalize();
// Ensure right-handed rotation.
real_t det = p_basis.determinant();
if (det < 0) {
p_basis.scale(Vector3(-1, -1, -1));
}
} I'd be fine with merging this as well, but there is some amount of wasted computation compared to what's in the PR currently. Also, note that since this |
I'll have to think about this more and ask a few friends. We want this Basis::Decompose to do three things.
That seems correct. The various methods should be unified in Basis in my opinion. Edited: I am not sure forcing right-handed rotation is correct. It feels wrong but I don't have an argument so... |
There might very well be some argument for adding/changing some stuff in
In case you haven't seen it, this is what Lines 406 to 410 in 9f68a81
And is what's being asserted in Lines 128 to 131 in 9f68a81
I'm frankly out of my depth here as well, but from what I understand it is in fact not "correct" to do this, because there is no one unique way of decomposing a negatively scaled matrix into rotation and scale, so you're forced to pick one arbitrarily. There's some reasoning for the way Godot does it here (which goes over my head a bit) as well as a mention of it here. |
Copying Lines 406 to 410 in 9f68a81
|
I've never tested this but I'm pretty sure that if you pass in a left-handed matrix into Jolt that everything will explode. |
I think there is, but you have to make sure that the sign of the scale that you 'extract' matches the flipping that you do to the rotation part. E.g. the code above doesn't do that:
If you decide to scale In an equation: M = R * S Where M is When the determinant is negative, you're doing: M = (R * F) * S where F is the diagonal matrix [-1, -1, -1], so in order to keep the matrix the same you need to do: M = (R * F) * (F^-1 * S) And since F^-1 = F this means you need to multiply your Since this code can be called quite often, I would opt for not wasting CPU cycles by doing duplicate work in |
Right, yeah, I get that it works out so long as you stick to whatever convention you pick. I was more referring to neither convention (of negating one versus three scales/axes) necessarily being more correct than the other (if that's even true).
Lines 323 to 324 in 9f68a81
Where Lines 289 to 294 in 9f68a81
So everything should be using the same convention in the code I suggested (I think), but clearly it could've used some comments to make that assumption explicit.
My vote is also (still) for keeping this PR as-is, since this is a somewhat hot code path. |
We'll roll with this as-is, I think the reasons given are adequate. Thanks! |
Cherry-picked for 4.4.1. |
…osition Fix broken negative scaling when using Jolt Physics
Fixes #103408.
Supersedes #103426.
This PR effectively just brings back some code (derived in part from Jolt's own
JPH::Mat44::Decompose
) that I had left behind when porting the Jolt integration from the Godot Jolt extension in #99895, due to wanting to keep things as minimal as possible.What this code is meant to do is take a potentially scaled
Basis
(orTransform3D
) and decompose it into a rotation part and a scale part, which must be done before passing any transform into Jolt, as Jolt takes scale separately from the rest of the transform in its API, and sometimes also takes rotation separately, in the form of a quaternion.I had mistaken the following code:
... as being a valid substitute for this code:
... which it is not, because while
Basis::orthonormalize
does also use the Gram-Schmidt process to perform the orthonormalization, much like this "new"decompose
function, it does not handle reflection (i.e. negative scaling) in any way, meaning we can end up with aBasis
that is orthonormal, but is not in fact a valid (right-handed) rotation, which causes problems with things likeBasis::get_quaternion
, as it assumes a valid (right-handed) rotation.This "new"
decompose
function solves this by essentially just flipping all the axes of theBasis
when we find ourselves with a negative determinant (i.e. negative scaling) which seems to line up with the convention used by otherBasis
methods, likeBasis::get_scale
andBasis::get_rotation_quaternion
.It also serves as a minor optimization, since we can avoid some of the redundant vector normalizations that are shared between
Basis::get_scale
andBasis::orthonormalize
.