-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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
Transform2D::interpolate_with
result produces unintended skew
#71974
Comments
the interpolate_with() will discard skew because transformations including skew are not common in Transform2D,3D. Actually, I thought that was a problem in 3D as well (see also #65131), but it is definitely a niche use case. It would be more make sense to construct the Basis ourself from the rotation and scale, and then interpolate the Basis. |
@TokageItLab Why did you tag this as enhancement? This is clearly a bug. Non-uniform scaling is not only a pretty common use case, it's a use case the function is supposed to support. Even if it wasn't, it should at least do something sensible, like force the scale to be uniform, not introduce a skew. |
Currently Since In other words, this issue can be read as a "proposal" that adding of a method to make it easy to linearly interpolate I know some methods for rotation are lacking in gdscript, see also #69202. However, if |
@TokageItLab This has nothing to do with which methods are lacking in GDScript, because this function is used internally by I've made a test with
The resulting two transformations are nearly identical (up to 5 decimal digits) So, even according to your criteria of needing both Incidentally, this adds another possible fix: Expand the As for MRP, the code sample I gave can be pasted basically anywhere for verification. Although, if absolutely necessary, I can create a project that uses P.S. Why would the 2D and 3D need to match anyway? The entire skeletal system is different between the two, with 2D using bones and nodes and modifications as data, while 3D uses bones as data and modifications as nodes. If they were supposed to match, that ship has sailed. |
Please send it and I can look into it.
This is for performance reasons godotengine/godot-proposals#1767. Also, the ModificationStack was not implemented in the proper way, so the architectural design is wrong. ModificationStack3D has already been removed and it is scheduled for major changes again in 4.1, and ModificationStack2D will eventually be outdated (those ModificationStack will become independent Node, not DataStack in Skeleton in the future plan). |
Which, for some reason, didn't apply to 2D, which just goes to show that 2D and 3D are different beasts.
Which may reduce one instance of the problem, depending on what it is replaced with. Specifically, depending on whether the internal use of
Took a bit of effort to make it clearly visible, because modification stack runs on every frame, and there's no way (or at least, I couldn't find one) to reduce the frame rate in the editor. To see the issue, go to the skeleton, and enable the modification stack. The stack is at strength 1.0, and contains only a two-bone IK, which should be deterministic. So, under normal circumstances, it should simply give the correct result on the first frame where it is activated. Instead, it actually goes through several states, several of which contain a skew, even though the modification is only supposed to apply a rotation. Incidentally, I could have demonstrated this more easily using a custom modification that simply sets the transform, but I preferred making a no-script example, to show that this isn't due to using the system in some unintended way, or having an error in the script. |
Thanks, I'll look into it later.
Node and MathClass are different. For example, methods with the same name in Vector2 and Vector3 should produce the same process. This also applies to Transform2D and Transform3D. |
Another possible fix is to replace the |
Haven't read deeply (pretty much just the title), just saying there's #64418 which you might wanna review/test. |
@kleonc Haven't tested, but I took one look at the code and it looks correct. Especially the part where it constructs the matrix directly, rather than in steps. I believe that PR would indeed fix this bug (In addition to making skew transforms functional as well). It's 4 months old, though, and is unclear if/when it will be merged. |
@TokageItLab Why is this still tagged "enhancement"? Did you look at the MRP I made? While #64418 does fix this, it is unlikely to be added to 4.0, since it (technically) adds functionality, and 4.0 is in feature freeze. This means that, without a fix for this, 4.0 would be released with at least one broken/unusable feature (Skeleton 2D modification stack) (This bug might influence other features. but I did not do an exhaustive search). Even if releasing 4.0 broken is "ok", at least give the issue the correct tag. It's a bug, not an enhancement. At the very least, it would be a "known bug" of 4.0. "Using 2D skeleton modifications on non-uniformly scaled bones causes unpredictable behavior". |
I looked at your MRP but it was unclear what you meant, so I stayed away for a while. I also had other priorities to work on, so I just started looking into this. So sorry. Yeah, it definitely seems to be broken, I just animated Transfomr2D and it is skewed. This is not good. At least this does not happen with Transform3D, so I definitely think it is a bug. Godot.2023.01.28.-.21.24.18.16.mp4There is already a PR as #64418, but I will have to ask @reduz if that is the right way to do it. As I commented on that, we may need to find the right way to rotate 2DBasis. In addition, it is described for consistency with Transform3D: "Transform2D interpolation without skew causes skew" This is a bug. It is clearly shown in the above video. "Transform2D interpolation with skew changes skew value" This is not a bug in one sense, but it is a bug in another sense: Transform3D::interpolate_with needs to be normalized in order to perform a slerp, and skew is discarded at this time. So it is consistent with Transform3D if Transform2D skew is set to 0, but it is wrong that skew becomes skew with a different value. However, it may not be a good thing in Transform2D for skew to be zero. In that case, it may be necessary to consider an algorithm that can perform a rotation including skew in Transform3D as well. |
Transform2D::interpolate_with
when the input has non-uniform scaleTransform2D::interpolate_with
result produces unintended skew
The way #64418 fixes 2D can also be used for 3D:
Generalized for 3D, this would be:
P.S. But it might not be the correct solution. In 2D, if you do angular lerp between two pairs of lines that are at a 90 degree angle of each other, you get a pair of lines that are at a 90 degree angle, because they are basically rotated by the same amount. In 3D, this guarantee does not hold, because of the non-Euclidean nature of the sphere. The mathematically correct method might be to SVD decompose the basis transform, do spherical lerp on the rotations matrices, and linear lerp on the scale matrix. |
Godot version
v4.0.beta14.official [28a2463] (But also present in master)
System information
Irrelevant
Issue description
Transform2D::interpolate_with
works by breaking twoTransform2D
s into rotation, scale, and translation, interpolating the two, then rebuilding a newTransform2D
from the interpolated values. However, the rebuild is wrong. This is the offending code:The transform is first created with a rotation, then scaled. However, the scale is done along the axes, not along the rotated vectors. If the scale is uniform, this doesn't matter. If it's non-uniform, this can actually introduce a skew into the result, which is pretty bad, considering the function actually discards skew.
The correct order should be backwards - First scale, then rotation. Alternately, replace the axis-aligned scale with a scale of the vectors themselves. Or add a function to build a matrix from rotation, scale, skew, and transform, using the formula
The bigger issue is that it has an effect even if
p_c
is1.0
, making classes that use it internally likeSkeleton2D
introduce bugs in code that should be fairly safe.A possible workaround is to add special cases for
p_c==0.0
andp_c==1.0
, as going through all the math, and introducing numerical instability and information loss in the middle, is pointless for those cases.Steps to reproduce
The interpolation value is
1.0
andsource.get_skew()
returns 0, yetinterpolated.get_skew()
returns-0.64350110292435
, even the the interpolation is supposed to erase skew, not create it.Minimal reproduction project
N/A
The text was updated successfully, but these errors were encountered: