Is there a bug in Quaternion(Vector3 arcFrom, Vector3 arcTo)?

Godot Version

Godot 4.5.1 mono

Question

A Quaternion created from axis vector (arcFrom) and direction vector (arcTo) , by Quaternion(Vector3 arcFrom, Vector3 arcTo) , measures rotation alone the y-axis.

Logically, the rotation of the axis vector (arcFrom) as Vector3.Right and as Vector3.Forward should be “symmetrical“. However an exception occurs in Vector3.Right condition.

Here are data of testing Quaternion(Vector3 arcFrom, Vector3 arcTo) :

C# code of the test is like:

var direction = new Vector(0f, 0f, -1f).Normalized();
var quaternion = new Quaternion(Vector3.Right, direction); // also use Vector3.Forward
var euler = quaternion.GetEuler();
... // print result

Afaik arguments to the constructor that takes 2 Vector3 arguments represent 2 points on the unit sphere. The actual rotation axis is then the cross product of those two vectors. Check the class reference.

use Quaternion(Vector3.Right, direction) :

use Quaternion(Vector3.Forward, direction) :

Since RIGHT and (-1.0, 0.0, 0.0) are colinear, the valid rotation axis can be any vector that’s perpendicular to x axis, including y and z axes. Godot probably defaults to some heuristics when choosing the axis here.

I tested it and the curious thing is that in Godot 4.3 the resulting quaternion is (0, 1, 0, 0) which I guess is what you’re expecting, but in 4.5 it’s indeed (0, 0, 1, 0). Again, both can be considered valid for opposing colinear vectors.

I suggest you use bases instead of quaternions if you need exact control of your object orientation. Or, if you want to stick with quaternions, check for this special case of colinear direction vectors and construct the quaternion with the explicit rotation axis, using the axis/angle constructor. In fact if you need to rotate around y axis in all of your cases, you can simply always use the axis/angle constructor.

Some calculation code in the quaternion class likely changed between 4.3 and 4.5. If you’re curious, maybe peek into the source code to see how colinear directions are handled for this constructor and what changed between versions.

2 Likes

How weird, it actually works normal in 4.4.1 . I’ve just copied 4.4.1 version source code and used in 4.5.1, then everything goes well. I think they make a mistake in 4.5.1 updating.

Creating Quaternion from Quaternion(Vector3 arcFrom, Vector3 arcTo) is just a short piece of C# code, which is easy to copy, use and test, doesn’t effect any Godot’s C++ code.

As suggested in official document, maybe quaternion is more convenient to cast rotation transform, but I did’t realize there is a mistake they missed in newer version.

Anyway, thanks for your reply that I can go back to 4.4.1 and make a test.

It’s not a mistake. It’s a difference. Mathematically, it’s equally valid. It’s impossible to unambiguously calculate the rotation axis for colinear vectors as cross product between them is zero. There is infinitely many vectors that are valid axes for such rotation operation.

As I said, use axis/angle constructor instead of arc/arc constructor.

3 Likes

Indeed this has been raised before, and I think the change from 4.4 is due to it being a discrepancy with GDScript and was fixed in 4.5, see `Quaternion(arc_from: Vector3, arc_to: Vector3)` behaves differently in gdscript and c# · Issue #107595 · godotengine/godot · GitHub

3 Likes

I misunderstood, sorry.

Finally, I’ve found a way to solve such problem, since like you said there are multi valid results mathematically, so the most “stable” way to create quaternion form an axis vector and a direction vector, is to judge two vectors parallel or not in first.

This is inspired from here: Quaternion(Vector3 arcFrom, Vector3 arcTo) may have a little mistake in Godot 4.5.1 mono · Issue #113881 · godotengine/godot · GitHub , as said:

rotation around parallel axes is unstable.

but the common solution in such cases is for the user to detect whether the axes are parallel and provide a 180-degree rotation around any axis.

In C# code anyone can do like this ( in godot 4.5.1 mono ):

// no need to normalize, 
// in 4.5.1 two vectors are both normalized in Quaternion construct function
var direction = new Vector(-1f, 0f, 0f); // use any value else

var axis = Vector3.Right;

// if direction is parallel to axis, use another axis
if (direction.Cross(axis).Length() == 0)
{
    axis = Vector3.Forward;
}

var quaternion = new Quaternion(axis, direction);

Those who want to use Quaternion(Vector3 arcFrom, Vector3 arcTo) no matter in C# or GDScrpit can take reference in same.

By the way, rotation direction is not same exactly when axis judge oder is different.

var axis = direction.Cross(Vector3.Right).Length() == 0 ? Vector3.Forward : Vector3.Right;


var axis = direction.Cross(Vector3.Forward).Length() == 0 ? Vector3.Right : Vector3.Forward;

the mesh:

You’d better make a test first if the rotation direction is required to be managed.

With one caveat, use is_zero_approx() instead of operator == to avoid floating point precision problems.

4 Likes

I’m not sure why you keep calling those two vectors axis and direction. They are not that. You seem to be having some misconceptions about what those two vectors represent, making you confused about the results you’re getting. Those two arguments represent two endpoints of the rotation arc placed on the unit sphere. Like q and q1 on the illustration below. The theta is the rotation arc/angle and the actual rotation axis is the normalized cross product of q and q1.

For every case where q and q1 are not colinear, there is a single unambiguous rotation axis that rotates q into q1 along the arc theta.

Now, if q and q1 are directly opposite, the angle becomes 180 degrees and rotating q around any axis perpendicular to either q or q1 will rotate it into q1 position. Hence there are infinitely many axes (and arcs) that will do the job.

However, in your case, you’re not rotating a singular point but the whole object/basis. So different rotation axes will result in different final orientation of the object, even though its position will always be properly rotated into place.

3 Likes

Ok, I finally realized mathematical meaning of these two vectors.

The reason why I keep calling them "axis“ and "direction“ is that I’m learning CatlikeCoding’ tutorial, which is a Unity project and I’m trying to implement the same in Godot. In Unity there is a function called Quaternion.FromToRotation, which is described as a direction rotate along an axis in the tutorial, and I found such a similar function in Godot. So I’m keeping misunderstanding the mathematical meaning of these two vectors.

Thanks for you correction that helps me getting out the misconception!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.