-
-
Notifications
You must be signed in to change notification settings - Fork 23.9k
Fix Quaternion tolerance to take into account accumulated 4 float errors #106335
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
80d23b4 to
235fb10
Compare
235fb10 to
e236f9e
Compare
|
I don't know about changing a core math function to fix a problem caused by the editor. Perhaps we need to increase the precision in the editor instead? |
|
Do you mean to precise the unit precision of the editor only for Quaternion? Although I don't think quaternions should be directly manipulated by humans, even if they were, it seems impractical to display and manipulate values in the editor with a precision more detail than Also, the unit precision of 0.001 is commonly used for properties of all classes; as long as the Vector3 editor's default unit is 0.001, it is meaningless to be only precise in the precision of the Quaternion elements. As commented in #106337, an error may occur within the constructor when generating a Quaternion from a non-Quaternion (although I think that should be normalized in the constructor, as in that PR #106337). So, I think it makes no sense to It seems that replacing If you set the epsilon to func _process(delta: float) -> void:
var q1: Quaternion = Quaternion(randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1)).normalized()
q1 = Quaternion(snappedf(q1.x, 0.001), snappedf(q1.y, 0.001), snappedf(q1.z, 0.001), snappedf(q1.w, 0.001))
var q2: Quaternion = Quaternion(randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1)).normalized()
q2 = Quaternion(snappedf(q2.x, 0.001), snappedf(q2.y, 0.001), snappedf(q2.z, 0.001), snappedf(q2.w, 0.001))
var q3: Quaternion = q1 * q2
if q3.is_normalized(): # <- always true
print(str(q3))
var q4: Quaternion = q1 * q2 * q1
if q4.is_normalized(): # <- can be false
print(str(q4))When func _process(delta: float) -> void:
var q1: Quaternion = Quaternion(randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1)).normalized()
q1 = Quaternion(snappedf(q1.x, 0.001), snappedf(q1.y, 0.001), snappedf(q1.z, 0.001), snappedf(q1.w, 0.001))
var q2: Quaternion = Quaternion(randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1), randf_range(-1, 1)).normalized()
q2 = Quaternion(snappedf(q2.x, 0.001), snappedf(q2.y, 0.001), snappedf(q2.z, 0.001), snappedf(q2.w, 0.001))
var q3: Quaternion = q1 * q2
if q3.is_normalized(): # <- can be false
print(str(q3))If performance is not a concern, I think it would be most accurate to adopt |
|
Superseded by #106352 |
For the Quaternion element in the Godot editor, the input precision of the float is 0.001. However, the accumulation of precision error in synthesizing four floats is not taken into account.
This PR ensures that the epsilon is determined taking into account the accumulation of errors. From a mathematical point of view, the worst-case error should be 0.004004, so the epsilon is 0.005 by ceil.
Calculation of accumulated error from 4 floats
The following code will be identified as not normalized in most cases before the application of this PR, but will pass the normalization check after the PR is applied.
test_quat_compose.gd
test_quat.zip
However, errors may still accumulate due to multiplication between quaternions.
This means that the result of multiplication between normalized quaternions may not be normalized. If the multiplication exceeds three times, normalize may collapse. This can be checked with the following gdscript.
test_quat_accum.gd
To solve this, it is safe to normalize the result of multiplication between normalized quaternions.
But I believe that this probably should not be done from a performance standpoint.
What we should find with care here is a function that directly returns the result of normalized Quaternion multiplication like
return q1 * q2;. If anyone find that, I think it should be fixed accordingly as follow up this PR.Also the fact that the multiplications between normalized Quaternion results may not be normalized, so that normalization is necessary may need to be documented, e.g., in the https://docs.godotengine.org/en/latest/tutorials/math/matrices_and_transforms.html.