Placing trees and bushes in an editor is fine for many types of games. There are many cases where you need objects to be in a very specific spot. When it comes to large-scale outdoor games, you might want to have a way of placing common objects in an automatic way, at least as a base. An artist or designer might then move items around to suit the needs of the game.
In this recipe, we'll create one such way that places trees using noise. Once the base is in, we'll take a look at how the pattern can be varied with different settings.
To produce automatic trees' distribution, perform the following steps:
TreeControl
that extends AbstractControl
.TerrainQuad
field called terrain
, a FractalSum
field called fractalSum
, a Spatial
field called treeModel
, and a BatchNode
field called treeNode
.setSpatial
method. Here, we declare treeNode
.Spatial
is a Node
class, parse its children looking for a Spatial
that is an instance of TerrainQuad
. Once found, set it to terrain
as follows:for(Spatial s: ((Node)spatial).getChildren()){ if(s instanceof TerrainQuad){ this.terrain = (TerrainQuad) s;
terrainSize
, create a nested for
loop statement that parses from its negative height and width to its positive.fractalSum
class based on the x and y coordinates. Then, look for the corresponding terrain height at that location as follows:float value = fractalSum.value(x, 0, y); float terrainHeight = terrain.getHeight(new Vector2f(x, y));
FractalSum
class generates a value between -1 and 1. Start by saying that any value above 0.5 should generate a tree and create an if
statement accordingly.treeModel
. Set its localTranslation
to the x and y coordinates and the current terrainHeight
field before attaching it to the treeNode
field:Spatial treeClone = treeModel.clone(); Vector3f location = new Vector3f((x), terrainHeight, (y)); treeClone.setLocalTranslation(location); treeNode.attachChild(treeClone);
treeNode
field to batch its contents to optimize the performance and then attach it to the supplied Spatial
.TestTerrainAdvanced
to get a start.Node
class called worldNode
, which we attach to rootNode
and then attach the terrain to.TreeControl
class and load and set a suitable model that we can use as treeModel
.TreeControl
class to worldNode
.After running the application, we will see trees spread out across the terrain—in valleys as well as on top of the mountains. Depending on the environment, trees might not grow on mountains. If we don't want this, we can add a simple check in the TreeControl
class. By adding a field called treeLimit
, we can clamp the growth of the tree above a certain height; also, make sure the terrainHeight
field is lower than the value supplied from fractalSum
.
In this example, we let the noise do most of the work for us. All we did was parse through the terrain, and at regular intervals, check whether the noise value at that point indicated whether a tree should be placed.
The noise provides an almost endless amount of variation to our distribution of vegetation and an equally endless amount of tweaking possibilities.
The drawback of using these automatic generation techniques is that we don't have proper control over them, and changing a value ever so slightly might have a large impact on the terrain. Also, even if the generation process is cheap and can be repeated deterministically, we will have to start storing the data as soon as we want to modify it in any way.
With the current settings, the example distributes trees across a landscape in a seemingly random pattern. At first glance, it might look natural but trees rarely are so evenly distributed as this. Outside of a forest, you will usually find trees clumped together. We can easily achieve this with noise by changing the frequency. The following examples show how changing the frequency can change the pattern:
3.146.176.145