TerrainGrid Heightmap not tied together properly

I’ve created a very basic heightmap (16bit grayscale as required) to create the issue:

For some reason the heightmap isnt matching together as it should. I’ve read a similar problem here:

http://hub.jmonkeyengine.org/forum/topic/a-couple-more-questions-about-terraingrid/

and that TerrainGrid is deprecated here:

http://hub.jmonkeyengine.org/forum/topic/endless-premade-terrain/

So before i “roll my own” terraingrid - is this a known issue, and is it true that terraingrid is deprecated - and is this one of the reasonss why? I ask because I don’t want to waste my time making my own if i am just missing a trick somewhere.

Many thanks.

TerrainGrid might be deprecated because it is difficult to extend and maintain but it works last I checked (generate 16-bit greyscale images also). This is most likely some mismatch in your code. For example bitmaps that are the wrong size (they must be n^2+1, for example 65x65 pixels) or float values that are NaN.
There are some tests for terrain grid in the jME source, you could have a look at those - might lead you to find what is wrong.

doh. forgot the + 1 - i was making 64x64 images… facepalm
I ended up creating my own grid anyway since it seems to give me a lot more freedom.

Probably inefficient at this stage, but ill share it nevertheless just in case anyone was interested. As far as I’m aware, you just need to adjust the bitshift var accordingly to get it to work with different sizes.

[java]
public class TerrainGenerator
{
private final GameContext context;

private final int viewDistance = 3;
private final int tileSize = 9;
private final int blockSize = 17;

private final int bitshift = 4;

private final List<Vector2f> loadedChunks;

private Material terrainMaterial;

public TerrainGenerator(GameContext context)
{
    this.context = context;
    this.loadedChunks = new ArrayList<Vector2f>();
    
    build();
}

private void build()
{
    /** 1. Create terrain material and load four textures into it. */
    terrainMaterial = new Material(this.context.getGame().getAssetManager(),
            "Common/MatDefs/Terrain/Terrain.j3md");

    /** 1.1) Add ALPHA map (for red-blue-green coded splat textures) */
    terrainMaterial.setTexture("Alpha", this.context.getGame().getAssetManager().loadTexture(
            "Textures/Terrain/splat/alphamap.png"));

    /** 1.2) Add GRASS texture into the red layer (Tex1). */
    Texture grass = this.context.getGame().getAssetManager().loadTexture(
            "Textures/Terrain/splat/grass.jpg");
    grass.setWrap(Texture.WrapMode.Repeat);
    terrainMaterial.setTexture("Tex1", grass);
    terrainMaterial.setFloat("Tex1Scale", 32f);

    /** 1.3) Add DIRT texture into the green layer (Tex2) */
    Texture dirt = this.context.getGame().getAssetManager().loadTexture(
            "Textures/Terrain/splat/dirt.jpg");
    dirt.setWrap(Texture.WrapMode.Repeat);
    terrainMaterial.setTexture("Tex2", dirt);
    terrainMaterial.setFloat("Tex2Scale", 32f);

    /** 1.4) Add ROAD texture into the blue layer (Tex3) */
    Texture rock = this.context.getGame().getAssetManager().loadTexture(
            "Textures/Terrain/splat/road.jpg");
    rock.setWrap(Texture.WrapMode.Repeat);
    terrainMaterial.setTexture("Tex3", rock);
    terrainMaterial.setFloat("Tex3Scale", 32f);
    
}

private Vector2f lastChunkLoc = null;

public void simpleUpdate(float tpf)
{
    
    // int xChunk = Math.round(context.getGame().getCamera().getLocation().getX()) >> bitshift;
    // int zChunk = Math.round(context.getGame().getCamera().getLocation().getZ()) >> bitshift;
    
    int xChunk = Math.round(context.getVehicle().getVehicleNode().getLocalTranslation().getX()) >> bitshift;
    int zChunk = Math.round(context.getVehicle().getVehicleNode().getLocalTranslation().getZ()) >> bitshift;
    
    Vector2f currentChunkLoc = new Vector2f(xChunk, zChunk);
    
    if (lastChunkLoc == null || currentChunkLoc.equals(lastChunkLoc) == false)
    {
        // set the new location
        lastChunkLoc = new Vector2f(xChunk, zChunk);
        
        List<Vector2f> surroundingChunkLocs = getSurrounding(lastChunkLoc);
        
        List<Vector2f> newChunks = new ArrayList<Vector2f>(surroundingChunkLocs);
        newChunks.removeAll(loadedChunks);
        
        List<Vector2f> oldChunks = new ArrayList<Vector2f>(loadedChunks);
        oldChunks.removeAll(surroundingChunkLocs);
        
        if (newChunks.isEmpty())
        {
            newChunks = surroundingChunkLocs;
        }
        
        // remove all old chunks
        for (int i = 0; i < oldChunks.size(); i++)
        {
            Vector2f thisChunkLoc = oldChunks.get(i);
            
            int chunkX = Math.round(thisChunkLoc.getX()) << bitshift;
            int chunkZ = Math.round(thisChunkLoc.getY()) << bitshift;
            
            String friendlyName = "chunk" + "_" + chunkX + "_" + chunkZ;
            
            TerrainQuad quad = (TerrainQuad)context.getGame().getRootNode().getChild(friendlyName);
            
            context.getGame().getRootNode().detachChild(quad);
            context.getGame().getPhysicsSpace().remove(quad);
            loadedChunks.remove(thisChunkLoc);
        }
        
        // load new chunks
        for (int i = 0; i < newChunks.size(); i++)
        {
            Vector2f thisChunkLoc = newChunks.get(i);
            
            int chunkX = Math.round(thisChunkLoc.getX()) << bitshift;
            int chunkZ = Math.round(thisChunkLoc.getY()) << bitshift;
            
            String friendlyName = "chunk" + "_" + chunkX + "_" + chunkZ;
            
            loadedChunks.add(thisChunkLoc);
            
            
            // add height map
            AbstractHeightMap heightmap = null;
            String materialName = "Textures/terrain-height/" + "hmap_" + chunkX + "_" + chunkZ + ".jpg";
            URL materialFile = AssetManager.class.getClassLoader().getResource(materialName);
            
            if (materialFile != null)
            {
                Texture heightMapImage = context.getGame().getAssetManager().loadTexture(materialName);
                heightmap = new ImageBasedHeightMap(heightMapImage.getImage());
            }
            
            TerrainQuad quad = new TerrainQuad(friendlyName, tileSize, blockSize, (heightmap == null) ? null : heightmap.getHeightMap());
            quad.setMaterial(terrainMaterial);
            quad.setLocalTranslation(chunkX, 0, chunkZ);
            
            quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), quad.getLocalScale()), 0));
            
            TerrainLodControl control = new TerrainLodControl(quad, context.getGame().getCamera());
            quad.addControl(control);
            
            quad.setShadowMode(RenderQueue.ShadowMode.Receive);
            
            context.getGame().getRootNode().attachChild(quad);
            context.getGame().getPhysicsSpace().add(quad);
            
            
        }
    }
}

private List<Vector2f> getSurrounding(Vector2f chunkLoc)
{
    int topLx = Math.round(chunkLoc.getX()) - viewDistance;
    int topLz = Math.round(chunkLoc.getY()) - viewDistance;
    
    int botLx = Math.round(chunkLoc.getX()) + viewDistance;
    int botLz = Math.round(chunkLoc.getY()) + viewDistance;
    
    List<Vector2f> results = new ArrayList<Vector2f>();
    
    for (int x = topLx; x <= botLx; x++)
    {
        for (int z = topLz; z <= botLz; z++)
        {
            results.add(new Vector2f(x, z));
        }
    }
    
    return results;
}

}
[/java]

3 Likes
@jayfella said: doh. forgot the + 1 - i was making 64x64 images.... *facepalm*

Been there and done that :slight_smile:
Thumbs up for sharing code.