Last active
April 17, 2024 16:06
-
-
Save unitycoder/8c632b39d0893a8d6c40 to your computer and use it in GitHub Desktop.
Latitude Longitude Position on 3D Sphere
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://forum.unity.com/threads/latitude-and-longitude-coordinates-in-vector-3-unity.1446328/#post-9066883 | |
static readonly float QUAD = .5f * MathF.PI; | |
static readonly float TAU = 2f * MathF.PI; | |
static public Vector2 FromPolarAngle(float theta) | |
=> new Vector2(MathF.Cos(theta), MathF.Sin(theta)); | |
static public float ToPolarAngle(Vector2 v) | |
=> Mod(MathF.Atan2(v.y, v.x), TAU); | |
/// <summary> Conversion from spherical to cartesian coordinates. </summary> | |
/// <param name="theta"> Polar angle 0..Tau (top-down). </param> | |
/// <param name="phi"> Azimuthal angle -Pi/2..+Pi/2 where 0 represents equator. </param> | |
/// <returns> A unit vector. </returns> | |
static public Vector3 FromSpherical(float theta, float phi) { | |
var th = FromPolarAngle(theta); var ph = FromPolarAngle(QUAD - phi); | |
return new Vector3(th.x * ph.y, ph.x, th.y * ph.y); | |
} | |
static public Vector3 FromSpherical(Vector2 coords) | |
=> FromSpherical(coords.x, coords.y); | |
/// <summary> Conversion from cartesian to spherical coordinates. | |
/// Returns <see langword="true"/> on success, <see langword="false"/> | |
/// otherwise. (Values are defined in any case.) </summary> | |
/// <param name="spherical"> The resulting spherical unit coordinates. </param> | |
/// <param name="magnitude"> Optional magnitude of the input vector. | |
/// Leave at 1 when input vector is unit to avoid normalization. </param> | |
static public bool ToSpherical(Vector3 v, out Vector2 spherical, float magnitude = 1f) { | |
var theta = MathF.Atan2(v.z, v.x); | |
theta = theta < 0f? theta + TAU : theta; | |
var im = (magnitude == 1f)? 1f : 1f / SubstZero(MathF.Max(0f, magnitude), float.NaN); | |
var phi = QUAD - MathF.Acos(v.y * im); | |
var success = true; | |
if(float.IsNaN(theta)) { theta = 0f; success = false; } | |
if(float.IsNaN(phi)) { phi = 0f; success = false; } | |
spherical = new Vector2(theta, phi); | |
return success; | |
} | |
static public float SubstZero(float v, float subst, float epsilon = 1E-6f) => MathF.Abs(v) < epsilon? subst : v; | |
static public float Mod(float n, float m) => (m <= 0f)? 0f : (n %= m) < 0f? n + m : n; | |
// You can now easily transform some 3D coordinates to longitude / latitude (in degrees) if you do | |
static public Vector2 ToLongLat(Vector3 coords, Vector3 center = default) { | |
coords -= center; | |
ToSpherical(coords, out var spherical, coords.magnitude); | |
if(spherical.x < 0f) spherical.x += 2f * MathF.PI; | |
return spherical * (180f / MathF.PI); | |
} | |
// And back (from longitude / latitude in degrees, to some exact 3D point on the surface) | |
// https://forum.unity.com/threads/latitude-and-longitude-coordinates-in-vector-3-unity.1446328/#post-9066883 | |
static public Vector3 FromLongLat(Vector2 longLat, Vector3 center = default, float radius = 1f) | |
=> center + radius * FromSpherical(longLat * (MathF.PI / 180f)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// blog post: https://unitycoder.com/blog/2016/03/01/latitude-longitude-position-on-3d-sphere-v2/ | |
using UnityEngine; | |
public class LatLong : MonoBehaviour | |
{ | |
public Transform marker; // marker object | |
public float radius = 5; // globe ball radius (unity units) | |
public float latitude = 51.5072f; // lat | |
public float longitude = 0.1275f; // long | |
// Use this for initialization | |
void Start() | |
{ | |
// calculation code taken from | |
// @miquael http://www.actionscript.org/forums/showthread.php3?p=722957#post722957 | |
// convert lat/long to radians | |
latitude = Mathf.PI * latitude / 180; | |
longitude = Mathf.PI * longitude / 180; | |
// adjust position by radians | |
latitude -= 1.570795765134f; // subtract 90 degrees (in radians) | |
// and switch z and y (since z is forward) | |
float xPos = (radius) * Mathf.Sin(latitude) * Mathf.Cos(longitude); | |
float zPos = (radius) * Mathf.Sin(latitude) * Mathf.Sin(longitude); | |
float yPos = (radius) * Mathf.Cos(latitude); | |
// move marker to position | |
marker.position = new Vector3(xPos, yPos, zPos); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://forum.unity.com/threads/calculate-latitude-and-longitude-using-uv-coordinates-of-a-sphere.1579935/#post-9775563 | |
// Convert UV coordinate to World Coordinate | |
public Vector3 UVTo3D(Vector2 uv, Mesh mesh, Transform transform) | |
{ | |
int[] tris = mesh.triangles; | |
Vector2[] uvs = mesh.uv; | |
Vector3[] verts = mesh.vertices; | |
for (int i = 0; i < tris.Length; i += 3) | |
{ | |
Vector2 u1 = uvs[tris[i]]; // get the triangle UVs* | |
Vector2 u2 = uvs[tris[i + 1]]; | |
Vector2 u3 = uvs[tris[i + 2]]; | |
// Calculate triangle area - if zero, skip it | |
float a = Area(u1, u2, u3); | |
if (a == 0) | |
continue; | |
// Calculate barycentric coordinates of u1, u2 and u3 | |
// If any is negative then point is outside the triangle, skip it | |
float a1 = Area(u2, u3, uv)/a; | |
if (a1 < 0) | |
continue; | |
float a2 = Area(u3, u1, uv)/a; | |
if (a2 < 0) | |
continue; | |
float a3 = Area(u1, u2, uv)/a; | |
if (a3 < 0) | |
continue; | |
// Point inside the triangle - find mesh position by interpolation… | |
Vector3 p3D = a1 * verts[tris[i]] + a2 * verts[tris[i + 1]] + a3 * verts[tris[i + 2]]; | |
// return it in world coordinates: | |
return transform.TransformPoint(p3D); | |
} | |
return Vector3.zero; | |
} | |
/// Calculate signed triangle area using a kind of “2D cross product”: | |
private float Area(Vector2 p1, Vector2 p2, Vector2 p3) | |
{ | |
Vector2 v1 = p1 - p3; | |
Vector2 v2 = p2 - p3; | |
return (v1.x * v2.y - v1.y * v2.x) / 2; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What was throwing me for a loop was I had some of the z/y values flipped around due to reading so many other sources that used a different coordinate handedness than Unity