At the last article, we already have our terrain rendered at out application. We can create some different types of terrain. In my application, I provide blank (flat) height map, hill height map, fractal height map, and fluid simulation height map. Of course we can’t just use the proceduraly generated height map to create our game world, although it looks quite nice and natural. We will want to have more control of where the ground is high, where the ground is flat, where the ground create a mountain, or create a small bump. In this article, I will explain how to make the tools that we created before work on our terrain.
And before I forget, the terrains that I created on the previous tutorial don’t have holes on their sides. Usually, terrains will have holes on their sides so we can see through their botton from their sides (not from below). It is because I modify the height map value after it is generated. I make that so every side or terrain edge has zero height.
Now we will start with the tools. But as always, I assume you have already done with the previous article.
It common for 3D application to have mouse picking. It is the action of detecting what object the mouse is clicking in the 3D world. I won’t give you detail explanation on how to do mouse picking, you can find it on the jME website. The difference is while in the ordinary jME mouse picking use the jME mouse handler, our world editor will use AWT mouse adapter (we have created one in the first article). The truth is we have used mouse picking at the second article where we create our brush, but looks like I forgot to mention it. From the AWT mouse handler, we can call a method from our implementor class and send the mouse coordinate to that method. That method will do the mouse picking and then do some action (remove and create brush).
In the case of the left mouse button is clicked, we will call another method which will also do the mouse picking but with different action. We will do some distance checking to find which vertex will be affected. For that, I have to check the distance of every vertex on the terrain with the position where our mouse is clicking. But I won’t use the actual distance. I ignore the y-axis so it means I assume the vertex and the mouse picked position are on the 2 dimentional space.
After getting the distance, I will check it with my brush radius. If the distance smaller than the brush radius (the vertex is inside the brush circle), I will use brush full strength to alter the the vertex position. If the distance is bigger than the brush radius but still smaller than the falloff radius, I will scale down the brush strength according to the distance, the farther, the smaller it becomes. It will create a nice interpolation on our terrain. You can choose to do linear interpolation or using some kind of formula to create non linear interpolation. What you have to remember is we want to get the distance of the vertex and the position of the vertex on the height map(the index of the vertex). We don’t need the actual position of the vertex on the terrain as we don’t need it to alter the terrain. For that, I have to scale down the actual vertex position by the terrain step scale. This is my code outline
Actually, I have to admit that my code is not efficient. But that’s what I got when I was creating it. I have no time yet to optimize it. But you can try to optimize it by yourself as long you can achieve the same result. Before I do any action, you can see in the code that I calculate the scale for the brush. I only use linear interpolation because it’s easier and faster to calculate (to compensate my other inefficient codes).
That method also receive ‘mode’. That is a variable to distinguish our action, whether it is raising terrain, lowering terrain, smoothing terrain, or the other action. I use bit mask for that variable because in my application I need it to have more than one modes at a time.
Then after we apply the brush change (after the loop), I write three statements. The first one is to update the terrain vertices from the height map. It is needed because what we actually modify is the height map, not the terrain’s vertices. The second statement will fix the normal vectors of every terrain vertex. If you don’t do that, you will see seams on the terrain after you alter it as you can see on the picture below. The third statement will update the collision tree for the terrain so it will act properly when we do mouse picking.
Now let’s do the action. I will explain six actions, raise, lower, flatten, smooth, noise, and set height.
Raise action and lower action are pretty much the same action. The only difference is in raise action we add the height of the terrain and in lower action we substract the height. Because of that, I only make one method to do both of them. It’s very simple, I only take the brush strength and multiply it with the scale of brush strength that we calculated based on vertex distance. Then add the result to the height map. If I want the terrain to lower, then I just have to pass the negative form of the scale.
These action are a little bit more complex than the previous one. The two of them are also very similar. They try to adjust the the height until some predefined height. The difference is flatten action will try to adjust the height to the heigth of the clicked terrain and set height will adjust it to an exact height defined by user. The action also need to consider if the vertex is higher than the target height, it has to reduce the height and vice versa. When I set the terrain height, I use brush strength as the target height and I use the clicked terrain as the target height when I flatten the terrain.
For this action, I think not to different with raising and lowering the terrain. The code is pretty much the same. The difference is when we raise or lower the terrain, we use brush strength value to add or substract the current height, but in noise action I use random number between 0 and brush strength – 1 to add the current height. It gives a little bumps when the brush strength value is small, but it creates a spike-like terrain when the value is big. You can also use another noise algorithm to create a more realistic noise.
This is the last and most complex action that I will explain in this article. I don’t know if this is the right method to smooth the terrain. But as far as I know, in image processing, smoothing process is implemented by blurring the image. I think our terrain can be assumed as an image (actually, the height map itself is a grayscale image that contain the height data of every vertex on the terrain). So I think we can accomplish smoothing on our terrain by blurring the terrain. If you search the blurring algorithm, you will find that to blur an image, we have to convolve the image with a matrix. This matrix can be in any size as long as it is odd number. But you have to remember the bigger the matrix is then the longer it will take to calculate it. So in this case, I only use 3×3 matrix. Then the value of each element of the matrix can be in any value. But a different value can lead to a different result. This algorithm can also be used to do sharpening, edge detection, or other image processing algorithm. For my application, I will use gaussian blur which use matrix calculated from gaussian formula. You can also find another matrix to use to get the similar result.
We can call all of those methods from the mouseClick() method that I write above. Of course we have to check the mode we are in at the time to decide what method we have to call. For short, it will be like this
Those are all the basic terrain deformation techniques that I can tell you right now. As I said before, I can’t guarantee the codes above will run efficiently. You can always try to optimize the code by yourself. Also, if you see the other existing world editor, maybe you can find another deformation techniques which I haven’t explained in this article. You can also try to figure out how they work and then implement them to your world editor. Actually I do have planned to add a tool to create a ramp. But I just don’t have time to do that yet.
For the next article I will explain about how we change the texture of our terrain, or maybe I should say how we paint it. I think it will be the most complicated work that I have done for this world editor. It’s because the TerrainPass itself still has the weaknesses (or maybe it’s just me who lack the required knowledge to do what I want).
© William Salim – WS08-1 – Software Laboratory Center