Пример #1
0
    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();
    }
Пример #2
0
    /// <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));
    }