[0.8.9] Terrain Generation

RawrRawr REGISTERED, Tester Posts: 510 Seed
edited January 2016 in Modding Tutorials
Getting Started
Welcome! So you'd like to start messing around with terrain? Excellent. We will begin with some of the files & folders:
Core/
	Data/
		Biomes/
		Generators/
		Generation/*deprecated*
		TerrainMaterials/
	Scripts/
		Core/NativeClasses.lua
		Generation/
	Materials/
		textures/
I'll explain each part as we go.
We'll also build upon an example script:
-------------------------------------------------------------------------------
if TutorialTest == nil then
	TutorialTest = EternusEngine.BiomeClass.Subclass("TutorialTest")
end

-------------------------------------------------------------------------------
function TutorialTest:BuildTree()
	local terrain = self:Constant(30)
	local materials = self:Material("Dirt")
	return terrain, materials
end

-------------------------------------------------------------------------------
TutorialTest.Lighting = {}
TutorialTest.Objects = {}
TutorialTest.Clusters = {}

-------------------------------------------------------------------------------
Eternus.ScriptManager:NKRegisterGeneratorClass(TutorialTest)
This particular script belongs it the Scripts/Generation folder. The BuildTree() function in it is used to define two things, the heightmap and materials. As this script is a subclass of the BiomeClass, we have access to all the BiomeClass functions within Scripts/Core/NativeClasses.lua . That is where self:Constant() and self:Material() come from.

Note: When making changes to your script, simply exiting and recreating your world will do.

Now, to get this generation script of ours working we'll need to make a couple of data files. One for the biome, name it anything.biome and put it in Data/Biomes.
Biomes
{
	TheTutorial
	{
		script = "TutorialTest.lua"
		weight = 1
		minSize = 1
		maxSize = 1
	}
}
One for the Generators, put anything_generator.txt into Data/Generators. Note that "Tutorial Testing" will appear in the Generator List when creating a new game.
Generators
{
	Tutorial Testing
	{
		TheTutorial
	}
}
While we're at it, we'll also create some layers for our biome in Data/TerrainMaterials. Name it anything_layers.txt
root
{
	Layers
	{
		TutorialTest
		{
			Tin Ore = 10
			Copper Ore = 15
			Gold Ore = 20
			Gravel = 25
			Cliff Rock Light = 40
			Cliff Rock Dark = 60
			Rock = 500
			Obsidian = 1000
		}
	}
}
All going to plan you should get some completely flat terrain with some interesting layers as you dig down.


Noise
In order to play with terrain we're going to need to grasp the concept of noise. Instead of showing in-game simplex noise, I've put together a diagram to visually show some perlin noise. Yes there is a difference between simplex and perlin noise but that mostly comes down to the mathematics behind it all, which is something beyond the scope of this tutorial. Seeing what's going on with one is transferable with the other.

Two pieces of information to help link this in with self:Simplex(scale, numOctaves) a.k.a the noise function. Above I've labeled 7 octaves, starting from the octave #1, this one has the smallest scale; Ending with octave #7 which has the largest scale. The Last image is the combination/average of all the other octaves, perlin noise. Remember, more octaves, more noise - smaller scale, larger "areas".

We'll adjust our script to the following and see what comes up...
function TutorialTest:BuildTree()
	local terrain = self:Constant(30)
	local mat1 = self:Material("Dirt")
	local mat2 = self:Material("Green Hills")
	local noise1 = self:Simplex((1/32)/1, 4)
	local materials = self:SwitchMaterial(mat1, mat2, noise1)
	return terrain, materials
end

Very nice, but still flat. So lets poke around at some of the other functions.

Variables and Functions
Before we do, the main things to note about the BiomeClass functions is that all of them can be used as variables and some of them can be used as functions with those variables. For example:
local terrain = self:Constant(30)
local noise1 = self:Simplex((1/32)/1, 3)
local x = self:Add(terrain, noise1)
local heightmap = self:Max(terrain, x)
This also means that you can return these variables from any function you create. This gives rise to making/including generator functions within your script to be later used inside of your BuildTree(). Be aware that some functions require extra properties to be set such as self:Switch() and self:Terrace(). Check the Eternus noiseModule for those extras.

Now, back to our script. Noise doesn't inherently know anything about height, we've got to explicitly add in that information. So, in order to morph the terrain with the current simplex we'll need a Multiply() function. We'll also pull down that constant value simply because we'll use it with a multiplier.
function TutorialTest:BuildTree()
	local terrain = self:Constant(5)
	local mat1 = self:Material("Dirt")
	local mat2 = self:Material("Green Hills")
	local noise1 = self:Simplex((1/32)/1, 4)
	terrain = self:Multiply(terrain, noise1)
	local materials = self:SwitchMaterial(mat1, mat2, noise1)
	return terrain, materials
end

The world very quickly starts to become your oyster. Giving the simplex a smaller scale would mean larger areas at which point adding in more octaves and increasing the constant could produce some hills like this:
function TutorialTest:BuildTree()
	local terrain = self:Constant(50)
	local mat1 = self:Material("Dirt")
	local mat2 = self:Material("Green Hills")
	local noise1 = self:Simplex((1/64)/6, 8)
	terrain = self:Multiply(terrain, noise1)
	local materials = self:SwitchMaterial(mat1, mat2, noise1)
	return terrain, materials
end
Materials
Textures, billboards, layers and materials are all defined within the TerrainMaterials folder. A material is a combination of textures and sometimes billboards. Layers are the materials that will occur below the surface of your terrain. Billboards are not the easiest thing to mod atm, it requires both Data/billboard.txt and/or Materials/textures/billboards.tga to be overridden depending on what you're wanting.

Objects
Density is the number of a particular object you'd like to spawn inside a cell (32 by 32 by 32 voxels). It can end up being less depending on: the angle of terrain, if any other object has already been placed and/or chance. To add the maple tree and pumpkin cauldron to our terrain, we need to edit the objects part of the script:
TutorialTest.Objects = 
{
	["Maple Tree"] =
	{
		density = 10,
		chance = 0.1
	},
	["Pumpkin Cauldron"] =
	{
		density = 5,
		chance = 0.1
	}
}

Cluster's are more or less the same but they do have a couple of extra variables you can define.

Biomes
It should be clear when to divide things into separate biomes, i.e. generation scripts, because you'll end up preferring some objects or types of terrain to not be in others. For example, a maple tree might look out of place in a desert.

That's it for the moment, any questions?

Edit: Oh and just in case these extra's are helpful to someone, here's a link to some older resources on terrain gen. The code for lastest version of the tutorial code is available here.
Programmer, designer, artist.
Sign In or Register to comment.