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> /// Teselates an icosphere but cutting each traingular face into four smaller traingular faces. /// </summary> /// <returns></returns> public Icosphere SubdivideSphere() { // List of all vertices currently in the icosphere List <SCoord> keys = new List <SCoord>(this.vertices.GetPoints()); BiDirectionalEdgeComparator <SCoord> edgeComparator = new BiDirectionalEdgeComparator <SCoord>(); // Set of all edges in the sphere (set to avoid duplicates) HashSet <Edge <SCoord> > edges = new HashSet <Edge <SCoord> >(edgeComparator); // For each vertex in the original icosphere foreach (SCoord point in vertices.GetPoints()) { // For each edge that this point is connected to foreach (SCoord endpt in vertices.GetConnected(point)) { // Create a structure for the edge Edge <SCoord> edge = new Edge <SCoord>(point, endpt); // Add the ege to the list of edges (Set to avoid duplicates) edges.Add(edge); } } // The graph of the subdivided sphere Graph <SCoord> subdivided = new Graph <SCoord>(vertices.GetPoints()); // List of all the points in the subdivided sphere List <SCoord> cuts = new List <SCoord>(edges.Count); List <Edge <SCoord> > edgeList = new List <Edge <SCoord> >(edges); Dictionary <Edge <SCoord>, SCoord> cutLookup = new Dictionary <Edge <SCoord>, SCoord>(edgeComparator); // For each edge in the sphere foreach (Edge <SCoord> edge in edgeList) { // Get the endpoints of the edge SCoord[] endpts = edge.GetPoints(); // Get the midpoint between the edge SCoord midpoint = SCoord.GetCentroid(endpts[0], endpts[1]); cutLookup.Add(edge, midpoint); // Add the point to the list of cuts cuts.Add(midpoint); // Put the new cut in the new sphere subdivided.AddPoint(midpoint); // connect the subdivided point to the points it cut apart subdivided.Connect(midpoint, endpts[0]); subdivided.Connect(midpoint, endpts[1]); } // For each new point added, connect it to other new points added for (int cutIdx = 0; cutIdx < cuts.Count; cutIdx++) { // Get the point and the edge it divides SCoord cut = cuts[cutIdx]; Edge <SCoord> originalEdge = edgeList[cutIdx]; // Get the edpoints on the original edge SCoord[] endpts = originalEdge.GetPoints(); // Find the other points in the graph that these two points have in common HashSet <SCoord> common = new HashSet <SCoord>(vertices.GetConnected(endpts[0])); common.IntersectWith(vertices.GetConnected(endpts[1])); List <SCoord> faceEdges = new List <SCoord>(common); // There should always be two of these points. This means for points that // should make up the original four edges. These plus the original edge // define the two faces that the four points share in common Edge <SCoord>[] commonEdges = { new Edge <SCoord>(faceEdges[0], endpts[0]), new Edge <SCoord>(faceEdges[0], endpts[1]), new Edge <SCoord>(faceEdges[1], endpts[0]), new Edge <SCoord>(faceEdges[1], endpts[1]) }; // Connect the subdivisions of these other edges to this cut foreach (Edge <SCoord> connected in commonEdges) { subdivided.Connect(cut, cutLookup[connected]); } } return(new Icosphere(center, radius, subdivided)); }