Weekly Update #1Thanks for all the encouragement, everyone!
This week I worked on refactoring some of the prototype code. My goal was primarily to push the planet generation code into a background thread. It's not apparent in the video, but it can take several seconds to generate a planet, and I really wanted to keep the smooth transitions from the Star Map into gameplay.
Since I don't have much new stuff to show, I figure it's a good time to go talk about how the planet generation algorithm works, which was the basis for this whole game.
Procedurally Generating Cartoon PlanetsI wanted to generate planets that could be used for a very stylish, non-photorealistic look. It needed to have clean, smooth shorelines, and raised/extruded landmasses, giving a fun exaggerated look, like these example references:
The "Videogame Planets" video by Tim Hijlkema was also an inspiration. If you haven't seen it, it's worth checking out:
In all of these references I made a mental list of qualities I was looking to emulate:
- Hard edges on shorelines, and cliffs
- Visually distinct regions (grass, sands, water), no blending
- Bright primary colors, with few or no textures
The traditional way of creating a planet is deforming a sphere with a displacement map, or a noise function. The concept is fairly easy. Subdivide a cube, or icosahedron to a desired tessellation. Then, offset each vertex along it's "up" vector (the vector from the center of the sphere to the vertex, which is really just it's normalized position) by a height value -- generated by a noise function, or a cubemap lookup. The problem is, this is really just a heightmap, which is really just a grid. Large variations in heightmaps create funny edges, and there was no nice way of getting those distinct edges I wanted between regions.
Elevation Slicing and ExtrusionAfter a lot of experimentation, I eventually came up with the idea of slicing the planet into layers based on elevation, like a
contour map.
My process is as follows:
- Generate an icosahedron
- Subdivide icosahedron using basic 1-to-4 "mid-point split" algorithm N times to create a sphere
- Using 3D simplex noise, assign a height value to each vertex, and translate it along it's "UP" vector (which is just it's normalized position, relative to the sphere center)
- Define a set of slicing "elevations"
- For each face of the planet that fall entirely above/below a slicing elevation, store them into corresponding containers
- If a face intersects a slice threshold, it needs to be split:
- Only one of the 3 verts will be on the opposite side of the slice threshold, meaning 2 edges need to be split
- Split the edges by adding new vert where the slice intersects the edge, based on height of the two end verts
- Generate the new faces, and put them into the corresponding containers
- Store the new edge that has been created (between the 2 new verts) in a linked-list of edges
The last thing that needs to be done is to order the edge lists, so they are consecutive, and form closed loops.
Now that the planet is sliced into layers, based on elevation, we can extrude the layers. Extruding is as simple as offsetting the layer's verts along their "up" vectors by some extrusion amount. Then, using the edge lists, generate quads that connect the edges of the layers together, to fill the gaps. The cool thing about the edge lists is that they can also be used to create shorelines, overhangs, or even as a path for geometry to follow.
Early development screens: geometry follows the edge loops to create cliff walls. Shoreline UVs animate (left) and wireframe view after slicing planet (right) I generate the white shorelines by creating a series of quads along the edges of the layers. The U component of the texture coordinates are accumulated around the loop, so they can be scrolled using a texture matrix animation.
Finally, the layers are populated with grass, trees and rocks (and whatever else) by iterating over a layer's faces, and placing a mesh near the center of the face, oriented to it's normal. I use a noise function to vary the density, and jitter the mesh locations slightly so it doesn't look completely uniform.
Next week I'm hoping to get physics integrated, at least enough to move forward with more gameplay prototyping. Thanks for reading!