-
-
Notifications
You must be signed in to change notification settings - Fork 21.7k
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
Core: Fix Basis::get_euler
incorrectly simplifying rotations in some cases.
#102144
Conversation
ed8143c
to
d56cbd3
Compare
BTW, this is not related to this PR, but I remember that Quaternion's |
My first thoughts are, why is this epsilon needed at all? (Don't get me wrong, it could be required for detecting gimbal lock, but would be nice to confirm.) If it is for I don't know if Here's a gemini generated version for comparison:
That said, this PR seems an improvement on the current behaviour and there's no reason we can't evaluate whether epsilons are required in a later PR. 👍 |
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.
With this code, in single-precision, this means that the value is allowed to be up to 3 ULPs away in order to pass the check and run the simpler code (the expression 1.0f - epsilon
is 4 ULPs away from 1.0f
). This is a fairly narrow margin of error if we expect to be working with values calculated from other things.
However, in this particular case, the point of these checks is to handle the common cases where Euler angle rotation happens in only one direction / around one axis with better accuracy. In those cases, we expect the values to be exact, because they shouldn't be altered (if you rotate in the XY plane around Z, then Z shouldn't be changed). This purpose is actually already evident by the == 0
checks under the // is this a pure Y rotation?
comments.
I wrote similar code last year for my 4D module for decomposing a Basis4D into 4D Euler angles (6 numbers), though it's simpler in that it only handles one Euler order. Suffice to say, the purpose of this code is pretty clear to me.
Anyway, this means that a more strict check should be absolutely fine with the expected use case. Even exact equality should still do the trick (as @lawnjelly mentions above). As the OP shows, the current check is too broad since it produces visual errors. Besides, the general code path still works even for the special cases. Therefore I think this is a good change, I don't think there are any downsides.
The only notes I have is that I would add to the comment to mention that 1.0 minus this value is 4 ULPs away from 1.0 in single precision, to give a 3 ULPs margin (maybe this can be worded more concisely / simply), and that @lawnjelly's comment above about potentially not needing an epsilon here at all is very valid too.
While I was trying to determine the ideal epsilon value, I actually did consider getting rid of it entirely. When I looked at the PR that introduced it though (#39483), the author claimed that it solved a nondescript "floating point issue." Without any other information on the actual issue, I figured it would be better to keep it and avoid a possible regression. I will change this if it's considered unnecessary. I will definitely go ahead and change those function calls to the Math versions though. |
Worth tagging @AndreaCatania and @RevoluPowered , I don't know if they are still contributing but they might remember why the epsilon was introduced. |
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.
We'll send it as-is for the time being. If we want global consistency in the future, there's always CMP_EPSILON2
; though that might be better suited for a PR focused on the math constants as a whole
Thanks! Congratulations on your first merged contribution! 🎉 |
Fixes #49839
A large testing threshold would sometimes cause
Basis::get_euler
to incorrectly assume a rotation could simplified. This would cause certain rotations with a range of +-0.26° to be effectively truncated, depending on the rotation order and axis. Using the MRP from #49839 for testing, I determined that0.00000025
was the largest threshold where the truncation was visually unnoticeable (Range of +-0.04°).