Hi,
I am having issue rotating a sphere (Earth) to make a point on it align with the cursor.
I am trying to find the shortest direction for the rotation.
For now, I am applying it directly and when that will work, I will change it to do it through time so that the earth rotates slowly along the shortest path to bring the point center.
I am testing with 2 points and the results are really varying.
The 2 points I am using are:
- Latitude 25.276514
Longitude 55.285213 - Latitude 25.780666f
Longitude -80.1785f
Note: In the screenshots, the gray transparent overlay is caused by the debug window I created with the data on the side.
This is the ‘origin’ point, when I start the application.
This is when I rotate to the first location from origin.
This is when I rotate to the second location from origin.
In both case, the purple dot should be on the cursor.
As you can see, in the case of the first location, the rotation is way south and slightly tilted. (you can say because the island of Madagascar should not be vertical, as it is slightly on the side in reality)
This is how I calculate the sphere radius:
// Get the radius of the Mesh unscaled
this._radius = _earth.GetComponent<MeshCollider>().bounds.extents.x / _earth.transform.localScale.x;
This is how I create the point location (if it should be green, not sure why it appears purple)
Location latest = ship.Locations[ship.CurrentLocation];
// Create a new sphere
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.name = ship.Name;
// Set its parent to PlanetEarth so that when we move/rotate/scale PlanetEarth everything will move/rotate/scale automatically
sphere.transform.parent = this._earth.transform;
// Compute the position of the sphere on earth
sphere.transform.localPosition = Quaternion.AngleAxis(latest.Longitude, -Vector3.up) * Quaternion.AngleAxis(latest.Latitude, -Vector3.right) * new Vector3(0, 0, this._radius);
sphere.transform.localScale = Vector3.one;
// Change the color
var renderer = sphere.transform.GetComponent<MeshRenderer>();
renderer.material.SetColor("_Color", Color.green);
The following code is what I use to find where the point should be moved to.
RaycastHit hitInfo;
Vector3 dir = transform.position - Camera.main.transform.position;
if (Physics.Raycast(Camera.main.transform.position, dir, out hitInfo, 1000))
{
/*
// Use this to center on Camera to Earth axis
// https://gamedev.stackexchange.com/questions/179931/help-needed-in-getting-the-correct-hit-position-a-raycast-hits-a-sphere-in-unity
this._point = hitInfo.collider.gameObject.transform.InverseTransformPoint(hitInfo.point);
this._currentCameraFocusLatLon = CartesianToPolar(this._point);
*/
// Use this to center on cursor
GameObject cursor = GameObject.Find("InteractiveMeshCursor");
this._currentCameraFocusLatLon = CartesianToPolar(hitInfo.collider.gameObject.transform.InverseTransformPoint(cursor.transform.position));
this._shipLatLon = new Vector2(location.Latitude, location.Longitude);
DebugWindow.Instance.AddLine(_currentCameraFocusLatLon.ToString());
DebugWindow.Instance.AddLine(_shipLatLon.ToString());
DebugWindow.Instance.AddLine("---");
}
private Vector2 CartesianToPolar(Vector3 point)
{
Vector2 polar;
//calc longitude
polar.y = 0 - Mathf.Atan2(point.x, point.z);
//this is easier to write and read than sqrt(pow(x,2), pow(y,2))!
float xzLen = new Vector2(point.x, point.z).magnitude;
//atan2 does the magic
polar.x = 0 - Mathf.Atan2(-point.y, xzLen);
//convert to deg
polar *= Mathf.Rad2Deg;
return polar;
}
And this is what I use in Update() to rotate
// https://gamedev.stackexchange.com/questions/136174/im-rotating-an-object-on-two-axes-so-why-does-it-keep-twisting-around-the-thir
// Yaw happens "over" the current rotation, in global coordinates.
float horizontal = ClosestToZero(this._currentCameraFocusLatLon.y, this._shipLatLon.y, 360, 180);
DebugWindow.Instance.AddLine(horizontal.ToString());
Quaternion yaw = Quaternion.Euler(0f, horizontal, 0f);
transform.rotation = yaw * transform.rotation; // yaw on the left.
// Pitch happens "under" the current rotation, in local coordinates.
float vertical = this._currentCameraFocusLatLon.x > this._shipLatLon.x ? this._currentCameraFocusLatLon.x - this._shipLatLon.x : this._shipLatLon.x - this._currentCameraFocusLatLon.x;
DebugWindow.Instance.AddLine(vertical.ToString());
Quaternion pitch = Quaternion.Euler(0f, 0f, -vertical);
transform.rotation = transform.rotation * pitch; // pitch on the right.
private float ClosestToZero(float a, float b, float range, float max)
{
if ((a < 0 && b >= 0) ||
(a >= 0 && b < 0))
{
a -= range;
}
float min = Math.Min(Math.Abs(a - b), Math.Abs(b - a));
if (a > b)
{
return 0 - min;
}
return min;
}
I tried using Quaternions and Slerp Rotation, but then no point at all would be where expected and the planet was seriously tilted.
Euler got me closer to what I want, but not for every points.