# Simple radians on Y axis from Quaternion rotation value.

I can’t figure this out.

I have a transform (player) moving over the surface of a planet. This means that up is sometimes down, and down is sometimes up. All I want is the local Y (up) rotation of a transform as it rotates around the Y axis (player turning left and right). The transform.rotation inverts itself and generally gives me gibberish as I walk around. I would be happy to have radians returned.

For a circle that lives on XY plane, its radius would be a segment going from its center to its circumference, perpendicular to some tangent line. Knowing this, you can always define a radial ray by taking a difference from any surface point and the center.

``````Vector2 radius = point - center;
``````

Once you have this segment, the gravity of the planet always points toward the center, collinear to radius. However, it is a direction (a vector of magnitude 1).

``````Vector2 gravity = -radius.normalized;
``````

For a sphere, its radius is very similar, however it is perpendicular to a tangent plane. Still, this segment is once again

``````Vector3 radius = point - center;
``````

You follow the same principle to get the gravity (or the down direction).

``````Vector3 gravity = -radius.normalized;
``````

If you now want a quaternion out of this, consider what is up to you. If it’s Vector3.up, then you really want a direction opposite to gravity, so `radius.normalized`.

Now you use Quaternion.FromToRotation to find the actual rotation for any point on the sphere.

``````var rotation = Quaternion.FromToRotation(Vector3.up, (point - center).normalized);
``````

However, this won’t work always, namely if your two vectors are antiparallel, the rotation is not defined.
To solve this, you just check whether the two directions are antiparallel, and impose a manual Quaternion that would make Vector3.down from Vector3.up, which is the desired solution.

``````var globalUp = Vector3.up;
var localUp = (point - center).normalized;
var dot = Vector3.Dot(globalUp, localUp);
if(1f + dot <= 1E-4f) return Quaternion.Euler(180f, 0f, 0f);
return Quaternion.FromToRotation(globalUp, localUp);
``````

I am trying to solve the rotation of the Green Y described in the video.

I can’t see the video (the link is broken).
Also I messed things up. This line should be edit: this change is a complete nonsense lol, just ignore Mathf.Abs (code below this line is reverted)

``````if(Mathf.Abs(1f + dot) <= 1E-4f) return Quaternion.Euler(180f, 0f, 0f);
``````

All in all, this was supposed to be a function, for example

``````Quaternion globalRotation(Vector3 point, Vector3 sphereCenter, Vector3 up) {
var localUp = (point - sphereCenter).normalized;
var dot = Vector3.Dot(up, localUp);
if(1f + dot <= 1E-4f) return Quaternion.Euler(180f, 0f, 0f);
return Quaternion.FromToRotation(up, localUp);
}
``````

You can then simply apply this to anything on your planet.

``````myCube.localRotation = globalRotation(myCube.position, Vector3.zero, Vector3.up);
``````

Or, if you want to apply it to other vectors, for example, a forward looking eyesight

``````myEyesightDirection = globalRotation(myBuddy.position, Vector3.zero, Vector3.up) * Vector3.forward;
``````

edit:
Also it’s important to note that Quaternion.Euler(180f, 0f, 0f) only works if your up vector wasn’t collinear with X axis to begin with, so this method can probably be made a bit smarter. I typically use my own version of FromToRotation to which I supply the antiparallel rotation in the call site.

Something like this would suffice (I think)

``````Quaternion globalRotation(Vector3 point, Vector3 sphereCenter, Vector3 up) {
var localUp = (point - sphereCenter).normalized;
var dot = Vector3.Dot(up, localUp);
if(1f + dot <= 1E-4f) {
var q = Quaternion.Euler(180f, 0f, 0f);
if((q * up - up).sqrMagnitude < 1E-7f) q = Quaternion.Euler(0f, 0f, 180f);
return q;
}
return Quaternion.FromToRotation(up, localUp);
}
``````

Mmnah it works, but something’s smelly. I’m sure there’s a more elegant approach, I would have to think some more about it.

Yeah right, you can simply check if the vector was global left or right, that’s better

``````Quaternion globalRotation(Vector3 point, Vector3 sphereCenter, Vector3 up) {
var localUp = (point - sphereCenter).normalized;
var dot = Vector3.Dot(up, localUp);
if(1f + dot <= 1E-4f) {
if(up.y == 0f && up.z == 0f) return Quaternion.Euler(0f, 0f, 180f); // a "rare" orthogonal case
return Quaternion.Euler(180f, 0f, 0f); // this might occur naturally, I mean antiparallels in general
}
return Quaternion.FromToRotation(up, localUp); // regular solution
}
``````

edit:
I say “rare” orthogonal case, because it should be rare probability-wise, HOWEVER, it is not rare because it is highly likely such a vector will be used deliberately.

I’ve seen the video now, and yes, that’s the solution I gave you (in post #5), including how to get the orientation around the local Y (the blue arrow; in post #4).

You are free to compound this local Y rotation like this

``````var someAngle = 45f;
myBuddy.localRotation = globalRotation(...) * Quaternion.Euler(0f, someAngle, 0f);
``````

Sorry about the Mathf.Abs nonsense, this is what sleep deprivation does to humans
It is completely redundant, I just got scared that I banned a half of the interval. No I did not, but when you write code from your head without checking any source, critical oversights like this one tend to creep up and I tried to cover my bases instead of just thinking straight.