Generate terrain base on gray scale image

I worte a small Class to handle custom Mesh for my own project. You can generate mesh based on Vector3f grid. If you can grab gray information of a pictrue, you can generate terrain meshs.



This is the gray scale map





This is the texture





This is the final scene





There are two parts of the codes.

This is the scene class, in which I grab the gray information form the gray scale map and setup the scene.


import com.jme3.app.SimpleApplication;
import com.jme3.asset.TextureKey;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.texture.Texture;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import javax.imageio.ImageIO;

/**
 * @author double1984
 */
public class Terrain extends SimpleApplication {

    private int[] pixelSamples;
    private int[][] pixelSample;
    private float tileLength = 0.1f;

    public static void main(String[] args) {
        Terrain te = new Terrain();
        te.start();
    }

    @Override
    public void simpleInitApp() {
        //read the gray scale map
        URL url = Terrain.class.getClassLoader().getResource("pathWalker/land.jpg");
        URI uri = null;
        try {
            uri = url.toURI();
        } catch (URISyntaxException ex) {
        }

        File file = new File(uri);

        BufferedImage bimage = null;
        try {
            bimage = ImageIO.read(file);
        } catch (IOException ex) {
        }

        int w = bimage.getWidth();
        int h = bimage.getHeight();
        //set the cam
        cam.setLocation(new Vector3f(w * tileLength / 2, w * tileLength / 2, h * tileLength));
        cam.lookAt(new Vector3f(w * tileLength / 2, 0, h * tileLength / 2), Vector3f.UNIT_Y);
        flyCam.setMoveSpeed(5);
        //get the gray samples
        pixelSamples = new int[h * w];
        pixelSample = new int[h][w];
        bimage.getRaster().getSamples(0, 0, w, h, 0, pixelSamples);
        int count = 0;
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                pixelSample[i][j] = pixelSamples[count];
                count++;
            }
        }
        //generate vectors based on the gray information
        Vector3f[][] vertex = new Vector3f[h][w];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                vertex[i][j] = new Vector3f(j * tileLength, pixelSample[i][j] / 50f, i * tileLength);
            }
        }
        //pass the vectors to the MultMesh object
        MultMesh mm = new MultMesh(vertex);
        mm.initMesh();
        //setup material
        Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/SimpleTextured.j3md");
        TextureKey key2 = new TextureKey("pathWalker/land2.jpg");
        key2.setGenerateMips(true);
        Texture tex2 = assetManager.loadTexture(key2);
        mat2.setTexture("m_ColorMap", tex2);
        //generate grometry
        Geometry mmG = new Geometry("mesh", mm);
        mmG.setMaterial(mat2);
        mmG.updateModelBound();
        mmG.updateGeometricState();

        rootNode.attachChild(mmG);
    }
}




This is the MultMesh Class which can generate mesh from a grid of Vector3f.


import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer.Type;

/**
 *
 * @author double1984
 */
public class MultMesh extends Mesh {
    //x,z方向的单元数量
    private int xUnit, zUnit;
    //x, z方向上的顶点数量
    private int xUp, zUp;
    //顶点数组
    private Vector3f[][] vertex;

    public MultMesh(Vector3f[][] pts) {
        vertex = pts;
        zUnit = pts.length - 1;
        xUnit = pts[0].length - 1;
        zUp = pts.length;
        xUp = pts[0].length;
    }

    //初始化多面
    public void initMesh() {

        this.setMode(Mode.Triangles);
        //储存所有顶点的坐标
        float[] positions = new float[xUp * zUp * 3];
        int count = 0;
        for (int i = 0; i < zUp; i++) {
            for (int j = 0; j < xUp; j++) {
                positions[count] = vertex[i][j].x;
                count++;
                positions[count] = vertex[i][j].y;
                count++;
                positions[count] = vertex[i][j].z;
                count++;
            }
        }
        this.setBuffer(Type.Position, 3, positions);

        //储存所有顶点的次序
        count = 0;
        int[] indexs = new int[xUnit * zUnit * 6];
        for (int i = 0; i < zUnit; i++) {
            for (int j = 0; j < xUnit; j++) {
                indexs[count] = i * xUp + j;
                count++;
                indexs[count] = i * xUp + xUp + j;
                count++;
                indexs[count] =  i * xUp + xUp + j + 1;
                count++;
                indexs[count] = i * xUp + j;
                count++;
                indexs[count] = i * xUp + xUp + j + 1;
                count++;
                indexs[count] = i * xUp + j + 1;
                count++;
            }
        }
        this.setBuffer(Type.Index, 3, indexs);

        //储存所有的贴图坐标
        count = 0;
        boolean xShift = true;
        boolean zShift = true;
        int texCoodSize = (xUnit + 1) * (zUnit + 1) * 2;
        float[] texCood = new float[texCoodSize];

        float xstep = 1f / (xUp - 1);
        float zstep = 1f / (zUp - 1);
        for (int i = 0; i < zUp; i++) {
            for (int j = 0; j < xUp; j++) {
                texCood[count] =j * xstep;
                count++;
                texCood[count] = 1-i * zstep;
                count++;
            }
        }
     
        this.setBuffer(Type.TexCoord, 2, texCood);

        //所有顶点的颜色
        count = 0;
        float[] vertexColors = new float[xUp * zUp * 4];
        for (int i = 0; i < xUp * zUp; i++) {
            vertexColors[count] = (float) Math.random();
            count++;
            vertexColors[count] = (float) Math.random();
            count++;
            vertexColors[count] = (float) Math.random();
            count++;
            vertexColors[count] = (float) Math.random();
            count++;
        }

        this.setBuffer(Type.Color, 4, vertexColors);
    }

    public Vector3f[][] getVertex() {
        return this.vertex;
    }
}

I tested your code and it works fine. It will be very useful for me and for creating some simple terrains. Thank you for your hard work ;).

I only replaced that PathWalker stuff with a direct path to the file.

Lockhead said:

I tested your code and it works fine. It will be very useful for me and for creating some simple terrains. Thank you for your hard work ;).
I only replaced that PathWalker stuff with a direct path to the file.


Err...  I forgot to change the "PathWalker" when I change the class name. Thank you ;)