@Bunny83
Before I say anything else, let’s keep the words “undefined” and “unexpected” as having separate meanings to clear out any misunderstandings.
a) I have a little bit of a history with this, and you can explore it a bit more if you’re interested. This is an old thread that got necro’d, I’ve got newer threads, and one of them is linked here.
b) I have a proof that this is an oversight, and not just a problem with mathematics. There is unexpected behavior down the line, I’m not sure why are you suggesting otherwise if there are comments about the unexpected behaviors. No it’s not similar to dividing by zero, until you delve a little deeper and see what’s going on. Dividing by zero would be undefined behavior, not unexpected.
c) I’ve managed to make a visual retestable proof of how exactly Unity does the wrong thing, if you only follow the instructions in that linked thread, and I’ve managed to introduce a workaround that prevents this behavior to occur in a controlled manner, so that it’s no longer unexpected, which was the issue with the Unity’s implementation.
d) As a sidenote, I’ve spent serious time investigating both Quaternions and Unity’s implementation of them in the last eight years or so. I know why this matters, and have more than one project where I’ve stumbled on this issue and know what I’m talking about. I think there is a snippet of code in this thread as well that showcases a bias and a clear discontinuity, for no apparent reason. Yes the mathematics is convoluted, and ends up hard to digest, but is very simple at its core, and should not lead to signs being flipped. This is why I resorted to making the proper environment to test this out.
This statement isn’t true because I’ve provided a correct behavior.
Any behavior that always acts in ONE way when presented with TWO or more choices is heavily biased, or at the very least heavily undocumented. Maybe you disagree with me, but in my book, such a bias should be considered a bug.
If a vendor is not at the counter at the moment, you do not fallback to a conclusion that the shop has no owner. That kind of thing. From a theoretical perspective, one could go even further and argue that ANY unexpected behavior should be declared a bug.
And FromToRotation is kind of a big deal when you work professionally in 3D. When you look at its implementation (you can’t really because it’s hidden from view, but I mean generally) it becomes painfully clear it’s one of the most fundamental things about working with Quaternions in general.
That said, notice how all of the people here, including me, have noticed a discontinuity (edit: there aren’t that many in this thread, admittedly, but I’ve had them elsewhere on the forum over the years, it’s hard to keep up). A quaternion function that simply yields undefined results because of undefined math, should not exhibit discontinuities (flipping particular signs), and in the worst case it should outright produce NaNs, which is a standard undefined behavior for numeric functions.
Again if you check out the fully featured Euler reconstruction I’ve made, you can test out exactly where Unity’s implementation gets this wrong. You even have step by step instructions of how to reproduce it with 100% certainty, and a side by side comparison!
Again, the link is here . If you have any trouble with the code, I’ll be glad to help.
Check out the next post for the standalone reproduction of this example.
I’ll also make sure to attach a GIF here, showcasing a clear case of biased code that yields unexpected behavior.
Appendix:
As a matter of fact, there is always a simple unique / correct solution, which is exactly why the result is unexpected, and btw Quaternion.Euler handles this correctly.
Furthermore, dividing by zero in this context always leads to a correct solution, and if you don’t care much about the actual path, it’s also always unique. It’s just that there are two paths to take, so it is ambiguous: rotation by axis 1 or rotation by axis 2 (edit: at least two, but sure, you could argue that there infinitely many, mathematically-speaking, but that doesn’t add anything of value implementation-wise). Quaternion.Euler handles this 100% correctly, while Quaternion.FromToRotation handles this 50% correctly. In certain contexts, because of the XZ plane bias, there is a 100% chance you’ll end up with a solution that was unexpected, for no reason…
Now this is obviously because Euler has a little bit of user context to gather data from, but the remedy is exactly that. Let the user add a little context for the added robustness of the function. You know, like LookAt has the worldUp and similar, or maybe there is a better analogy, but you get the point. Let the user resolve potential ambiguities by optionally supplying context.
I am well aware that FromToRotation is supposed to be plain mathematical function that can backfire when used in an ambiguous context (antiparallel or null vectors), however it should just as well produce values that are consistent with said ambiguity and that are indicative to an out of bounds scenario, not just flip the signs and be like ‘done’ with both hands in the air.
It should either:
a) produce NaN values (standard behavior)
b) throw an error (one might argue it would unnecessarily lower the performance)
c) let the user provide additional parameters that might prevent any discontinuities (likely the best trade off)
On top of this, whatever the solution, it should be thoroughly documented.
This is why I’m seeing the current implementation as buggy, or at least as an example of a bad/rushed design of the fundamentals, just like having Vector3s implicitly cast to Vector2s violates any sane programming specification, silently dropping data like it’s nothing.
Now we can argue about this, but at least refrain from doing so until you’ve checked my example.
edit:
Oh, I forgot to include that the code in the original post fixed any apparent discontinuities even though it also had to work with antiparallel vectors. At this point I can’t really tell whether I just added a different bias, or if it did in fact improved the behavior across the table, but it was clear from the get go that having an unexpected discontinuity is not the same as having undefined result / ambiguous rotation, and that there are ways to handle this better.
Quaternions are 4-dimensional spheres – how on Earth do you even introduce discontinuities with them? Without an opportunity to actually see the code, I can only guess, but my hunch is that there is a badly formed IF block in there, throwing a wrench in an otherwise beautiful mathematics.