Connecting terrein nodes

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
diho
Posts: 46
Joined: Fri May 20, 2011 9:01 pm
Location: Netherlands

Connecting terrein nodes

Post by diho »

Hello everyone,

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:

Image

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;
            }
        }
 
These two lines need to be added:

Code: Select all

 
if (j < 16 || j > (count - 16) || j % 16 == 0 || (j % 15) == (floor(j / 15) - 1))
                    continue;
 
resulting in the following code snippet:

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;
            }
        }
 
Compile the engine, compile your code with the new libIrrlicht.a and this will be the result:

Image

Explanation:

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

Image

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
For making this a complete story, here is the code for achieving this. (with the modified CTerrainSceneNode.cpp). It is a modified version of Terrain Render Example (no. 12). I also added a compiled version for linux users to test.
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
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Re: Connecting terrein nodes

Post by christianclavet »

Thanks Diho! I'll look at this! It may improve the terrain in IRB as we are using a custom terrain node without LOD.
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Connecting terrein nodes

Post by CuteAlien »

I have added a function ITerrainSceneNode::setFixedBorderLOD to svn trunk r5610. This allows to handle this is a clean way.
It's more or less doing what the patch proposed, but not always enabled and also working for different sized terrains (above code assumed 16x16 and also missed some border patches). Thanks for report, testcase and patch-idea, sorry for taking so long.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Post Reply