In my own project I needed to connect multiple terrain nodes to each other. In the process of accomplishing this I came across a few difficulties. Luckily I found the solutions, which I now want to share with you.
First of all you need a big heightmap. In this example I am using a heightmap of 512x512. Now you need to split the heightmap in parts. For this example I split it in 2x2 parts. Make sure that the edges of the individual parts that should connect to an other part share one row/column of pixels with it's neighbor. Doing so results in 4 parts of a heightmap with each a size of 257x257 (256 + 1 pixel extra for the shared pixel row/column). Repeat this process for the texture image.
Now you can simply loading the heightmap parts in seperated terrain nodes and align them so they connect to each other.
This will result in the following:

If you look closely, you can see that there are a few gaps at the seams where the terrain nodes connect to each other. This happens when you are at a certain distance to the seam. It is a result of the LOD system of irrlicht. At a certain distance one terrain node has LOD level i, and the one after that will have LOD level i+1 because it has a longer distance to the camera.
The solution to this problem is simply adding two lines to CTerrainSceneNode.cpp around line 640, there you will find the following:
Code: Select all
for (s32 j = 0; j < count; ++j)
{
if (frustum->getBoundingBox().intersectsWithBox(TerrainData.Patches[j].BoundingBox))
{
const f32 distance = cameraPosition.getDistanceFromSQ(TerrainData.Patches[j].Center);
TerrainData.Patches[j].CurrentLOD = 0;
for (s32 i = TerrainData.MaxLOD - 1; i>0; --i)
{
if (distance >= TerrainData.LODDistanceThreshold[i])
{
TerrainData.Patches[j].CurrentLOD = i;
break;
}
}
}
else
{
TerrainData.Patches[j].CurrentLOD = -1;
}
}
Code: Select all
if (j < 16 || j > (count - 16) || j % 16 == 0 || (j % 15) == (floor(j / 15) - 1))
continue;
Code: Select all
for (s32 j = 0; j < count; ++j)
{
if (frustum->getBoundingBox().intersectsWithBox(TerrainData.Patches[j].BoundingBox))
{
const f32 distance = cameraPosition.getDistanceFromSQ(TerrainData.Patches[j].Center);
TerrainData.Patches[j].CurrentLOD = 0;
//border patches should be the same
if (j < 16 || j > (count - 16) || j % 16 == 0 || (j % 15) == (floor(j / 15) - 1))
continue;
for (s32 i = TerrainData.MaxLOD - 1; i>0; --i)
{
if (distance >= TerrainData.LODDistanceThreshold[i])
{
TerrainData.Patches[j].CurrentLOD = i;
break;
}
}
}
else
{
TerrainData.Patches[j].CurrentLOD = -1;
}
}

Explanation:
We need to make sure that the patches at the borders of terrain nodes all have the same LOD level.

So for j < 16 || j > (count - 16) || j % 16 == 0 || (j % 15) == (floor(j / 15) - 1), where j is the index of an array with patches inside a terrain node.
- j < 16 selects all the red patches
- j > (count - 16) selects all the green patches
- j % 16 == 0 selects all the yellow patches
- (j % 15) == floor(j / 15) - 1) selects all the blue patches
Please don't comment on the code, I know it can be done much nicer, but it's just for clarity's sake.
https://bitbucket.org/dihomc/irrlicht-connecting-terrain-nodes/src
If you have questions or additions, feel free to ask/comment.
-Diho