Trying to fix the multiple GeoMipTerrain gap issue

Hello All,
I spent a whole week researching different ideas to fix the gap issue that appears when two copies of the same terrain are joined together. My first thought was I am doing something wrong. I double checked Gimp and my HeightField generator. Both seemed fine. Then I tested different terrain heights. Lower heights give smaller gaps. (I also notice that lower heights give smaller deviations with get_elevation). Then I read the forums to find out that this is a known issue. The possible solutions that I saw involved masking. My car flips over when I hit a masked gap.

Anyway, my first thought was to somehow edit the terrain edges. I knew this would be very complicated, so I checked other possible solutions first. Now I am back to editing the edges. The debug wireframe mode shows vertices that should be matching up. At first I wanted to match two different terrain models. Then I had the idea of calibrating one terrain with itself. The left edge should match the right edge and the front edge should match the back edge. If a difference is found, then reset the Z-value to the midway point.

While the concept sounds great, I can not find a implementation method. All I can find is editing the geom-nodes. All my attempts to access the geometry of the terrain (or any object) has failed.


# Heightfield (static)
height = 16.0

img = PNMImage(Filename('models/desert-dunes-2.png'))
shape = BulletHeightfieldShape(img, height, ZUp)
shape.setUseDiamondSubdivision(True)

np = self.worldNP.attachNewNode(BulletRigidBodyNode('Heightfield'))
np.node().addShape(shape)
np.setPos(0, 0, 0)
np.setCollideMask(BitMask32.allOn())

self.world.attachRigidBody(np.node())

self.terrain = GeoMipTerrain('terrain')
self.terrain.setHeightfield(img)

self.terrain.setBlockSize(4)
self.terrain.setNear(40)
self.terrain.setFar(100)

rootNP = self.terrain.getRoot()
rootNP.reparentTo(render)
rootNP.setSz(height)

mytex = loader.loadTexture("models/ground.jpg")
mytex.setWrapU(Texture.WM_repeat)
mytex.setWrapV(Texture.WM_repeat)
rootNP.setTexScale(TextureStage.getDefault(),1024,1024)
rootNP.set_texture(mytex)

offset = img.getXSize() / 2.0 - 0.5
rootNP.setPos(-offset, -offset, -height / 2.0)

self.terrain.generate()

Stitching together two terrains just requires the number of vertices (ie. LOD level) on the edges to map. You can call self.terrain.setBorderStitching(True) to make sure the edge tiles all have the lowest LOD level, ensuring that it always matches up with surrounding tiles with that flag set, but of course this is a bit less efficient.

There is an easier way of making the gaps disappear, which is to create extra geometry extruding downwards at the edges of the terrain, while the normals still remain pointing upwards. This is what ShaderTerrainMesh does and it’s good enough to hide any gaps visually. GeoMipTerrain doesn’t currently implement this, however.

I did try the self.terrain.setBorderStitching(True) command. It did not help the gaps much. It did make the rest of the joints look invisible. This is already in the documents and forums. I find it NOT good enough.

My request is more specific. I am not looking for alternatives. I want to edit the vertices on the terrain edge. I want to understand how this can be done.

Early in my career, I was taught Nothing is Impossible. The only real issue is What does it take to make it happen. I am a firm believer in this philosophy.

Maybe later we can add an optional function. self.terrain.calibrateTerrainEdge(True)

@rdb - I noticed that you have been here a long time with lots of great feedback.
I appreciate any help that I can get on this topic. I think you have done something
very similar to what I am trying to do. I am hoping a few pointers is all I need.

I tried to add Geom test code to access the Geom data.

rootNP = self.terrain.getRoot()
rootNP.reparentTo(render)
rootNP.setSz(height)

gnp = rootNP.find('**/+GeomNode')
gn = gnp.node()
for i in range(gn.getNumGeoms()):
  print ("Geom %s:" % (i))
  print (gn.getGeomState(i))
  gn.getGeom(i)
  gn.getGeom(i).getVertexData()

This fails with the following message. I tried other similar test. All fail.

gn = gnp.node()

AssertionError: !is_empty() at line 228 of built1.11/include/nodePath.I

What am I doing wrong here???

Use this to get the vertex data for an individual block:

np = self.terrain.getBlockNodePath(0, 0)
geom = np.node().modifyGeom(0)
vdata = geom.modifyVertexData()

Then you can use GeomVertexRewriter to modify the data, as explained in the manual.

Note that you need to do this after you’ve called generate().

The after terrain.generate() issue was the cause of my error. Thanks.