/// <summary> /// Sorts a set of three spherical coordinates in clockwise order /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <param name="s3"></param> /// <returns>The ordered arary of vertices in clockwise order</returns> public static SCoord[] SortClockwiseOrder(SCoord s1, SCoord s2, SCoord s3) { Vector3 v1 = s1.ToEuclidian(); Vector3 v2 = s2.ToEuclidian(); Vector3 v3 = s3.ToEuclidian(); Vector3 centroid = (v1 + v2 + v3).normalized; Vector3 n = Vector3.Cross(v2 - v1, v3 - v1); float w = Vector3.Dot(n, v2 - centroid); if (w > 0) { return(new SCoord[] { s1, s2, s3 }); } return(new SCoord[] { s3, s2, s1 }); }
// Start is called before the first frame update void Start() { // Make the icosphere sphere = new Icosphere(transform.position, radius); for (int sub = 0; sub < subdivsions; sub++) { sphere = sphere.SubdivideSphere(); } // Make the faces of the icosphere foreach (SCoord coord in sphere.Coordinates) { // Get the 3d coordinate of the sphere Vector3 point = sphere.GetPoint(coord); // Get the rotation of the face to make it tangent to the sphere Vector3 rotation = SCoord.GetRotation(coord); // Get the kind of object GameObject newObj = null; int degree = sphere.GetDegree(coord); if (degree == 5) { newObj = Instantiate(pentagonPrefab); } else { newObj = Instantiate(hexagonPrefab); } // Place and rotate object tangent to the sphere newObj.transform.eulerAngles = rotation; newObj.transform.position = point; newObj.transform.SetParent(this.transform); // Rotate the face to be line up correclty IEnumerator <SCoord> neighbors = sphere.GetNeighbors(coord).GetEnumerator(); neighbors.MoveNext(); SCoord neighbor = neighbors.Current; Vector3 targetVec = Vector3.ProjectOnPlane(neighbor.ToEuclidian() - coord.ToEuclidian(), newObj.transform.up).normalized; float angle = Mathf.Acos(Vector3.Dot(newObj.transform.right.normalized, targetVec)); if (float.IsNaN(angle)) { angle = 180; } Vector3 cross = Vector3.Cross(newObj.transform.right.normalized, targetVec); if (Vector3.Dot(newObj.transform.up.normalized, cross) < 0) { angle *= -1; } angle *= 180 / Mathf.PI; Debug.Log(angle); newObj.transform.Rotate(0, angle, 0, Space.Self); tiles.Add(coord, newObj); } }
/// <summary> /// Gets the rotation of an object that would be on the surface of the sphere at this coordinate. /// </summary> /// <param name="coord">coordinate on the sphere</param> /// <returns>Rotation of an object if it was placed on the sphere at this coordinate<returns> public static Vector3 GetRotation(SCoord coord) { return(Quaternion.FromToRotation(Vector3.up, coord.ToEuclidian()).eulerAngles); }
/// <summary> /// Gets the angle between two coordiantes on a sphere. /// </summary> /// <param name="coord1">First coordinate on the sphere. (From)</param> /// <param name="coord2">Second coordinate on the sphere. (To)</param> /// <returns>The angle representing the direction between the two coordinates in radians.</returns> public static float GetAngleBetween(SCoord coord1, SCoord coord2) => Mathf.Acos(Vector3.Dot(coord1.ToEuclidian(), coord2.ToEuclidian()));
public static void RenderTile(Mesh mesh, SCoord tileCenter, Icosphere hexSphere) { // Make a linked list of the coordinate's neighbors LinkedList <SCoord> neighbors; // hexes are not triangles neighbors = new LinkedList <SCoord>(hexSphere.GetNeighbors(tileCenter)); // Make list of vertices List <Vector3> vertices = new List <Vector3>(neighbors.Count + 1); // Make list of normals for each vertex List <Vector3> normals = new List <Vector3>(neighbors.Count + 1); // Make list of UV coordinates for each vertex List <Vector2> uvLocations = new List <Vector2>(neighbors.Count + 1); // Setup lookup table for vertices Dictionary <SCoord, int> keyLookup = new Dictionary <SCoord, int>(); keyLookup.Add(tileCenter, keyLookup.Count); uvLocations.Add(new Vector2(0.5f, 0.5f)); vertices.Add(hexSphere.GetPoint(tileCenter)); normals.Add(tileCenter.ToEuclidian()); float radPerVertex = Mathf.PI * 2 / neighbors.Count; SCoord startVertex = neighbors.First.Value; SCoord source = startVertex; neighbors.RemoveFirst(); // Get the order of neighbors (forward or backward just wrapping around center) LinkedList <SCoord> orderedNeighbors = new LinkedList <SCoord>(); orderedNeighbors.AddLast(startVertex); // Calculate the teselated edges of the hexagon/pentagon while (neighbors.Count > 1) { // Find the next SCoord in the sequence of neighbors (neighbor to origin that is // adjacent to the previous neighbor) LinkedListNode <SCoord> nextVertex = neighbors.First; while (!new List <SCoord>(hexSphere.GetNeighbors(nextVertex.Value)).Contains(source)) { nextVertex = nextVertex.Next; } orderedNeighbors.AddLast(nextVertex.Value); // Save current vertex as previous vertex source = nextVertex.Value; // Remove current vertex so it is not checked again neighbors.Remove(nextVertex); } // Finish list of ordered neighbors orderedNeighbors.AddLast(neighbors.First.Value); foreach (SCoord n in orderedNeighbors) { keyLookup.Add(n, keyLookup.Count); } List <SCoord> neighborList = new List <SCoord>(orderedNeighbors); List <int> triangleList = new List <int>(); for (int idx = 0; idx < neighborList.Count; idx++) { // Get the centroid of the three adjacent tiles SCoord vert = SCoord.GetCentroid(tileCenter, neighborList[idx], neighborList[(idx + 1) % neighborList.Count]); // Sort the coordinates in the correct order so the face is visible SCoord[] triangleCoords = SCoord.SortClockwiseOrder(tileCenter, neighborList[idx], neighborList[(idx + 1) % neighborList.Count]); // Add the vertex vertices.Add(hexSphere.GetPoint(vert)); // Add the normal vector of the vertex normals.Add(vert.ToEuclidian()); // Add UV coordinate for this vertex Vector2 uv = new Vector2(0.5f + Mathf.Cos(radPerVertex * idx) * 0.5f, 0.5f + Mathf.Sin(radPerVertex * idx) * 0.5f); uvLocations.Add(new Vector2(0.5f + Mathf.Cos(radPerVertex * idx) * 0.5f, 0.5f + Mathf.Sin(radPerVertex * idx) * 0.5f)); // Add the triangles (set of three vertices) triangleList.Add(keyLookup[triangleCoords[2]]); triangleList.Add(keyLookup[triangleCoords[1]]); triangleList.Add(keyLookup[triangleCoords[0]]); } // Flatten out the center of the hex so it doesn't arch out Vector3 flatCenter = Vector3.zero; for (int i = 1; i < vertices.Count; i++) { flatCenter += vertices[i]; } flatCenter /= (vertices.Count - 1); vertices[0] = flatCenter; // assign values to mesh mesh.vertices = vertices.ToArray(); mesh.normals = normals.ToArray(); mesh.triangles = triangleList.ToArray(); mesh.uv = uvLocations.ToArray(); }
/// <summary> /// Gets the euclidian coordinate of a point around the center of this icosphere. /// </summary> /// <param name="coordinate">Coordinate in lattitude and lnogitude relative to the spehre</param> /// <returns>The point at that lattitude and longitude the radius of the icosphere /// away from the center of the icosphere.</returns> public Vector3 GetPoint(SCoord coordinate) => coordinate.ToEuclidian() * radius + center;