# Terrain: Fixing the holes caused by dropping the LOD

This topic is 4845 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I have an array of floats (512x512) which represent the heightmap. What I do is then pass the coordinates (0,0) and (512,512) to my function that renders the terrain's "land". If all of the quad is outside the view bail out. Otherwise, check to see if it is lower quality (in terms of size - so 512x512 is low quality!) than the current allowable LOD (calculated based on distance from the player to the centre of the quad). If it isn't, just draw a quad across those four points, if it is then split it into four smaller quads and call the function on each of those four smaller ones. This works pretty well and pretty fast apart from one thing - when a set of larger quads are drawn next to a bunch of smaller split quads there is a "hole" where they don't line up properly. (The central edge of the larger quad is not at the correct height). Is there an easy way around this? The method seemed to be the easiest and clearest to me when I wrote it, but I can't see a simple way to fix those holes!

##### Share on other sites
I don't think there is a very easy way around it. One of the most used solutions is adding 'skirts' around that lod-level. That should give you some ideas if you google for it.

##### Share on other sites
see
http://vterrain.org/LOD/Papers/index.html
or
http://vterrain.org/LOD/Implementations/index.html

##### Share on other sites
If I understand you correctly the problem is called T-junction problem.

Lower LOD

------
|\ |
| \ |
| \ |
| \|
------

Higher LOD

---------
|\ |\ |
| \ | \ |
| \| \|
---------
|\ |\ |
| \ | \ |
| \| \|
---------

One soultion is to make this split instead

Higher LOD #2

--------
|\ /|
| \ / |
| \/ |
| /\ |
| / \ |
|/ \|
--------

##### Share on other sites
only pushes the basic problem one LOD up:

next LOD:

-----
|\|/|
|---|
|/|\|
-----
^will cause cracks!

##### Share on other sites
Quote:
 Original post by hooomerseehttp://vterrain.org/LOD/Papers/index.htmlorhttp://vterrain.org/LOD/Implementations/index.html
Those look useful, thanks. [smile]

##### Share on other sites
there are essentially 2 approaches to solve cracks: 1) add skirts to fill cracks, 2) use different indices for different LODs / neighbours.

try this for skirts and this for a clever index trick.

##### Share on other sites
I tackled this problem in my engine. I found a not so simple solution. I'll paste in some code, if you can follow it, and please don't pick out the faults it was an early morning hack:

This class basically generate a triangle fan for me, a lot more hacky and specific case than it should be, but it works for the mean time:

class cTriangleFan
{
private:

public:
unsigned int m_CenterX;
unsigned int m_CenterZ;

unsigned int m_pSides[4][16];

int m_NumOnSide[4];

cTriangleFan()
{
for (int i=0; i<4; ++i)
m_NumOnSide = 0;
}

eGenericError Initilise(int l_NumOnSide[4], int l_CenterX, int l_CenterZ)
{
for (int i=0; i<4; ++i)
{
m_NumOnSide = l_NumOnSide;
}

m_CenterX = l_CenterX;
m_CenterZ = l_CenterZ;
return SUCCESS;
}

unsigned int ReturnIndex(int i, int j, int pitch)
{
if (i >= pitch)
i = pitch-1;
if (j >= pitch)
j = pitch-1;
if (i < 0)
i = 0;
if (j < 0)
j = 0;

return (i * pitch) + j;
}

void GenerateTriangles(unsigned int* l_pIndices, int& l_Index, int l_ArrayPitch, int l_LODFactor)
{
// Loop through sides
for (int i=0; i<4; ++i)
{
int l_Iter = (l_LODFactor*2) / (m_NumOnSide - 1);

// Loop through points - 1
for (int j=0; j<m_NumOnSide-1; ++j)
{
switch(i)
{
case 0:
l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+0)*l_Iter)), m_CenterZ-l_LODFactor, l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+1)*l_Iter)), m_CenterZ-l_LODFactor, l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+0)*l_Iter)), m_CenterZ-l_LODFactor, l_ArrayPitch); ++l_Index;
//l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+1)*l_Iter)), m_CenterZ-l_LODFactor, l_ArrayPitch); ++l_Index;

break;
case 1:
l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+1)*l_Iter)), m_CenterZ+l_LODFactor, l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+0)*l_Iter)), m_CenterZ+l_LODFactor, l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+1)*l_Iter)), m_CenterZ+l_LODFactor, l_ArrayPitch); ++l_Index;
//l_pIndices[l_Index] = ReturnIndex(m_CenterX+(-l_LODFactor+((j+0)*l_Iter)), m_CenterZ+l_LODFactor, l_ArrayPitch); ++l_Index;

break;
case 2:
l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX-l_LODFactor, m_CenterZ+(-l_LODFactor+((j+1)*l_Iter)), l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX-l_LODFactor, m_CenterZ+(-l_LODFactor+((j+0)*l_Iter)), l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX-l_LODFactor, m_CenterZ+(-l_LODFactor+((j+0)*l_Iter)), l_ArrayPitch); ++l_Index;
//l_pIndices[l_Index] = ReturnIndex(m_CenterX-l_LODFactor, m_CenterZ+(-l_LODFactor+((j+1)*l_Iter)), l_ArrayPitch); ++l_Index;
break;
case 3:
l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX+l_LODFactor, m_CenterZ+(-l_LODFactor+((j+0)*l_Iter)), l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX, m_CenterZ, l_ArrayPitch); ++l_Index;
l_pIndices[l_Index] = ReturnIndex(m_CenterX+l_LODFactor, m_CenterZ+(-l_LODFactor+((j+1)*l_Iter)), l_ArrayPitch); ++l_Index;

//l_pIndices[l_Index] = ReturnIndex(m_CenterX+l_LODFactor, m_CenterZ+(-l_LODFactor+((j+1)*l_Iter)), l_ArrayPitch); ++l_Index;
//l_pIndices[l_Index] = ReturnIndex(m_CenterX+l_LODFactor, m_CenterZ+(-l_LODFactor+((j+0)*l_Iter)), l_ArrayPitch); ++l_Index;
break;
};
}

}

}
};

This is the code my terrain patch class uses to generate a patch where the edges match nicely with adjacent patches:

void cTerrainFragment::Generate()
{
int index=0;
m_ArrayPitch = 512;
int l_NewSideLOD;

if (m_LODFactor == m_LODPrev)
{
// Ok, we havent changed, have our neighbours?
bool changed=false;
for (int i=0;i<4;++i)
{
// Has it changed?
if (m_pSides!=NULL)
{
l_NewSideLOD = m_pSides->m_LODFactor;
if (m_SideLOD != l_NewSideLOD)
{
m_SideLOD = l_NewSideLOD;
changed = true;
}
}
}
if (!changed)
return;
}

m_LODPrev = m_LODFactor;

int l_SizeX = ((m_MaxX - m_MinX) / m_LODFactor) / 2;
int l_SizeZ = ((m_MaxZ - m_MinZ) / m_LODFactor) / 2;

int l_NumOnSide[4];
cTriangleFan l_Fan;

for (int i=0; i<l_SizeX; ++i)
{
for (int j=0; j<l_SizeZ; ++j)
{
// Get LOD for each side
for (int k=0; k<4; ++k)
{
l_NumOnSide[k] = (m_LODFactor / (GetSideLOD(k, i ,j, l_SizeX, l_SizeZ)) * 2) + 1;
}
// Pass in some info
l_Fan.Initilise(l_NumOnSide, m_MinX + m_LODFactor + (i*m_LODFactor*2), m_MinZ + m_LODFactor + (j*m_LODFactor*2));

// Add individual triangles to index list
l_Fan.GenerateTriangles(m_pIndices, index, m_ArrayPitch, m_LODFactor);

}
}

m_NumIndices = index;
}

Actually come to think of it, pick my code to bits its a hacky mess which was just for testing purposes for now.

##### Share on other sites
I'm currently just wasting some time in a computer lab, so I don't really have much access to resources or links or anything, but I did find logs for an IRC lecture I held awhile back about terrain engines.

http://andyc.org/lecture/viewlog.php?log=Future%20of%20Terrain%20Programming

I'm near-positive that I discussed crack-fixing techniques at some point in the lecture. There should be some decent information (or, perhaps, even links) but, you know, it's been a couple years. My memory may be fading.

##### Share on other sites
What I do is to shift around the indices of the higher detail patches so that they don't create cracks. I'm a little vague on exactly how your system works though, so I'm not sure if this is appropriate or not...if you want more details visit me in IRC.

1. 1
Rutin
73
2. 2
3. 3
4. 4
5. 5

• 21
• 10
• 33
• 20
• 9
• ### Forum Statistics

• Total Topics
633426
• Total Posts
3011812
• ### Who's Online (See full list)

There are no registered users currently online

×