C++ Terrain Generation
Now that I have working hex terrain generation prototype working in Unreal’s Blueprint system, it is time to throw it all in the bin and start over in C++. Why you ask? For a few reasons I answer. I like the Blueprint system a lot and it does seem pretty fast but ultimately I’m looking to build an infinite procedural world using hex-based chunks. I’m certain Blueprints can do it… and I’m pretty sure C++ can do it a lot faster. Additionally I want as much control over the procedural generation of the mesh, the subdividing and calculation of the height data as I can get and C++ will guarantee this too. Besides, with the complexity of the system I’m sure a Blueprint to handle what I want to do will end up looking like a lot of spaghetti and difficult to maintain. It’ll be much easier to handle things like caching of vertices and meshes in code. So why Blueprints in the first place? Mostly quick prototyping, feeling out the style and deciding if Unreal was actually the engine to do this in rather than Unity (I had already started on this project in Unity but had abandoned it for hopefully greener pastures).
First we start with a hexagon. It is a very smol hexagon, but it is my hexagon and it is much loved. It is created in code by using sine and cosine to calculate the xy coordinates every 60 degrees. Size doesn’t matter at this point since everything can be scaled up with transforms later so it’s just a 1 unit radius (which are centimetres in Unreal).
(EDIT: I have come to regret this very much. Later on when try to fix a degenerate triangle problem I trace the problem all the way back to only using a 1 unit radius for the mesh. After subdividing so many times the distance between the vertices get so small that Unreal’s physics engine throws a wobby and can’t generate the normals. I end up scaling everything up to 100 units - so 1 metre in Unreal)
Once we have our mesh we can begin the process of writing our subdivision code. Subdiving triangles is easy as you don’t need to move any vertices in space or memory at all. Each triangle becomes 4 triangles like so.
So all you have to do is get your midpoints, add them to the end of your vertex array, rewrite the original triangle indexes to use the new midpoints, and then add 3 new sets triangle indexes to the end to make the outer triangles. Easy peasy, right? Add a bit of recursion with decrementing depth as a parameter and we have the following.
From here it’s just a matter of creating some new materials to work with the mesh to provide the custom wireframe effect I’m going for. The mesh UVs need to be specially prepared to make it work as well (I will definitely write up a much more detailed article both the subdivision implementation and the shader creation in the Project Hex section of this site). This time I create a new material specially for the ocean-OH GOD I’M BLIND!
Unreal’s lighting system literally flashbangs me in the face when I try to apply a translucent water shader to my ocean mesh. I think it is at this point I go into the project’s global settings and start disabling Lumen, bloom, exposure, motion blur and anything else I can find that I don’t want in this project :)
Once I get the materials sorted I head back into the world of Perlin noise and write some preliminary reusable functions to calculate my height data for my terrain and set the heights during the mesh creation and subdivision code. While I was doing all of the above, I was exposing most of my methods and properties to the Unreal editor and also overriding the property update methods so I could change the values in the editor and they’d be instantly reflected in the mesh.
I play around with the Level’s Blueprint to control these properties over time to create a sort of demo and this is the result.
Please keep in mind, every time the terrain’s noise scale or translation is changed, ALL of the vertices have their Z coordinate updated with a new height calculation (basically once per frame). It’s fast. Blazingly fast. I don’t even need this level of functionality for the game idea I have in mind, but it’s nice to know I can create a sort of fluid and deformable mesh that’s happy at runtime. Also, this is all CPU computation as well. None of it is on the GPU and so there is definitely room for optimisation and improvemenet.
Anyway, I think that’s enough for this post. Thanks for reading!