Creating Procedural Planets In Unity — Part 2

A semi-arid world, created by extruding polygons to create oceans and multiple layers of hills.
public class Polygon
{
public List<int> m_Vertices;
public List<Polygon> m_Neighbors;
public Color32 m_Color;
public bool m_SmoothNormals;
public Polygon(int a, int b, int c)
{
m_Vertices = new List<int>() { a, b, c };
m_Neighbors = new List<Polygon>();
// This will determine whether a polygon's normals smoothly
// blend into its neighbors, or if it should have sharp edges.
m_SmoothNormals = true;
// Hot Pink is an excellent default color because you'll
// notice instantly if you forget to set it to something else.
m_Color = new Color32(255, 0, 255, 255);
}
public bool IsNeighborOf(Polygon other_poly)
{
int shared_vertices = 0;
foreach (int vertex in m_Vertices)
{
if (other_poly.m_Vertices.Contains(vertex))
shared_vertices++;
}
// A polygon and its neighbor will share exactly
// two vertices. Ergo, if this poly shares two
// vertices with the other, then they are neighbors.
return shared_vertices == 2;
}
// Mr. Roger's voice: Please won't you replace my neighbor. public void ReplaceNeighbor(Polygon oldNeighbor,
Polygon newNeighbor)
{
for(int i = 0; i < m_Neighbors.Count; i++)
{
if(oldNeighbor == m_Neighbors[i])
{
m_Neighbors[i] = newNeighbor;
return;
}
}
}
}
public class Planet
{
...

public void CalculateNeighbors()
{
foreach (Polygon poly in m_Polygons)
{
foreach (Polygon other_poly in m_Polygons)
{
if (poly == other_poly)
continue;

if (poly.IsNeighborOf (other_poly))
poly.m_Neighbors.Add(other_poly);
}
}
}
}
public class Edge
{
// The Poly that's inside the Edge. This is the one
// we'll be extruding or insetting.
public Polygon m_InnerPoly;
// The Poly that's outside the Edge. We'll be leaving
// this one alone.
public Polygon m_OuterPoly;
//The vertices along this edge, according to the Outer poly.
public List<int> m_OuterVerts;
//The vertices along this edge, according to the Inner poly.
public List<int> m_InnerVerts;
public Edge(Polygon inner_poly, Polygon outer_poly)
{
m_InnerPoly = inner_poly;
m_OuterPoly = outer_poly;
m_OuterVerts = new List<int>(2);
m_InnerVerts = new List<int>(2);
//Find which vertices these polys share.
foreach (int vertex in inner_poly.m_Vertices)
{
if (outer_poly.m_Vertices.Contains(vertex))
m_InnerVerts.Add(vertex);
}
// For consistency, we want the 'winding order' of the
// edge to be the same as that of the inner polygon.
// So the vertices in the edge are stored in the same order
// that you would encounter them if you were walking clockwise
// around the polygon. That means the pair of edge vertices
// will be:
// [1st inner poly vertex, 2nd inner poly vertex] or
// [2nd inner poly vertex, 3rd inner poly vertex] or
// [3rd inner poly vertex, 1st inner poly vertex]
//
// The formula above will give us [1st inner poly vertex,
// 3rd inner poly vertex] though, so we check for that
// situation and reverse the vertices.
if(m_InnerVerts[0] == inner_poly.m_Vertices[0] &&
m_InnerVerts[1] == inner_poly.m_Vertices[2])
{
int temp = m_InnerVerts[0];
m_InnerVerts[0] = m_InnerVerts[1];
m_InnerVerts[1] = temp;
}
// No manipulations have happened yet, so the outer and
// inner Polygons still share the same vertices.
// We can instantiate m_OuterVerts as a copy of m_InnerVerts.
m_OuterVerts = new List<int>(m_InnerVerts);
}
}
public class EdgeSet : HashSet<Edge>
{
// Split - Given a list of original vertex indices and a list of
// replacements, update m_InnerVerts to use the new replacement
// vertices.
public void Split(List<int> oldVertices, List<int> newVertices)
{
foreach(Edge edge in this)
{
for(int i = 0; i < 2; i++)
{
edge.m_InnerVerts[i] = newVertices[ oldVertices.IndexOf(
edge.m_OuterVerts[i])];
}
}
}
// GetUniqueVertices - Get a list of all the vertices referenced
// in this edge loop, with no duplicates.
public List<int> GetUniqueVertices()
{
List<int> vertices = new List<int>();
foreach (Edge edge in this)
{
foreach (int vert in edge.m_OuterVerts)
{
if (!vertices.Contains(vert))
vertices.Add(vert);
}
}
return vertices;
}
}
public class PolySet : HashSet<Polygon>
{
//Given a set of Polys, calculate the set of Edges
//that surround them.
public EdgeSet CreateEdgeSet()
{
EdgeSet edgeSet = new EdgeSet();
foreach (Polygon poly in this)
{
foreach (Polygon neighbor in poly.m_Neighbors)
{
if (this.Contains(neighbor))
continue;

// If our neighbor isn't in our PolySet, then
// the edge between us and our neighbor is one
// of the edges of this PolySet.
Edge edge = new Edge(poly, neighbor);
edgeSet.Add(edge);
}
}
return edgeSet;
}
// GetUniqueVertices calculates a list of the vertex indices
// used by these Polygons with no duplicates.
public List<int> GetUniqueVertices()
{
List<int> verts = new List<int>();
foreach (Polygon poly in this)
{
foreach (int vert in poly.m_Vertices)
{
if (!verts.Contains(vert))
verts.Add(vert);
}
}
return verts;
}
}
public class Planet
{
....
public List<int> CloneVertices(List<int> old_verts)
{
List<int> new_verts = new List<int>();
foreach(int old_vert in old_verts)
{
Vector3 cloned_vert = m_Vertices [old_vert];
new_verts.Add(m_Vertices.Count);
m_Vertices.Add(cloned_vert);
}
return new_verts;
}
}
public class Planet
{
....
public PolySet StitchPolys(PolySet polys)
{
PolySet stichedPolys = new PolySet();
var edgeSet = polys.CreateEdgeSet(); var originalVerts = edgeSet.GetUniqueVertices(); var newVerts = CloneVertices(originalVerts); edgeSet.Split(originalVerts, newVerts); foreach (Edge edge in edgeSet)
{
// Create new polys along the stitched edge. These
// will connect the original poly to its former
// neighbor.
var stitch_poly1 = new Polygon(edge.m_OuterVerts[0],
edge.m_OuterVerts[1],
edge.m_InnerVerts[0]);
var stitch_poly2 = new Polygon(edge.m_OuterVerts[1],
edge.m_InnerVerts[1],
edge.m_InnerVerts[0]);
// Add the new stitched faces as neighbors to
// the original Polys.
edge.m_InnerPoly.ReplaceNeighbor(edge.m_OuterPoly,
stitch_poly2);
edge.m_OuterPoly.ReplaceNeighbor(edge.m_InnerPoly,
stitch_poly1);
m_Polygons.Add(stitch_poly1);
m_Polygons.Add(stitch_poly2);
stichedPolys.Add(stitch_poly1);
stichedPolys.Add(stitch_poly2);
}
//Swap to the new vertices on the inner polys.
foreach (Polygon poly in polys)
{
for (int i = 0; i < 3; i++)
{
int vert_id = poly.m_Vertices[i];
if (!originalVerts.Contains(vert_id))
continue;

int vert_index = originalVerts.IndexOf(vert_id);
poly.m_Vertices[i] = newVerts[vert_index];
}
}
return stichedPolys;
}
}
public class Planet
{
....
public PolySet Extrude(PolySet polys, float height)
{
PolySet stitchedPolys = StitchPolys(polys);
List<int> verts = polys.GetUniqueVertices();
// Take each vertex in this list of polys, and push it
// away from the center of the Planet by the height
// parameter.
foreach (int vert in verts)
{
Vector3 v = m_Vertices[vert];
v = v.normalized * (v.magnitude + height);
m_Vertices[vert] = v;
}
return stitchedPolys;
}
}
public class Planet
{
....
public PolySet Inset(PolySet polys, float interpolation)
{
PolySet stitchedPolys = StitchPolys(polys);
List<int> verts = polys.GetUniqueVertices();
//Calculate the average center of all the vertices
//in these Polygons.
Vector3 center = Vector3.zero;
foreach (int vert in verts)
center += m_Vertices[vert];
center /= verts.Count;
// Pull each vertex towards the center, then correct
// it's height so that it's as far from the center of
// the planet as it was before.
foreach (int vert in verts)
{
Vector3 v = m_Vertices[vert];
float height = v.magnitude;
v = Vector3.Lerp(v, center, interpolation);
v = v.normalized * height;
m_Vertices[vert] = v;
}
return stitchedPolys;
}
}
public class Planet
{
....

public PolySet GetPolysInSphere(Vector3 center,
float radius,
IEnumerable<Polygon> source)
{
PolySet newSet = new PolySet();
foreach(Polygon p in source)
{
foreach(int vertexIndex in p.m_Vertices)
{
float distanceToSphere = Vector3.Distance(center,
m_Vertices[vertexIndex]);

if (distanceToSphere <= radius)
{
newSet.Add(p);
break;
}
}
}
return newSet;
}
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store