# Creating custom Mesh dynamically

I made a class that creates a natural looking random terrain using an algorithm.
The size of the map is as follows:
int DETAIL = 9;
int SIZE = (int)Math.pow(2, DETAIL) + 1; // SIZE is a power of 2, plus one

So, that class generates the heights array.
That is a float[] array.
When I want to get the height at a certain point on the ground, I convert the x and y map coords
to the heights array index as follows:

``````float h = heights[x + SIZE * y];
``````

I have another class called Terrain which extends Mesh.
I use that class to get a custom mesh object.
Here is the code of that class:

``````package terrain;

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

public class Terrain extends Mesh {

public Terrain() {

}

public void setHeights(float[] heights, int mapsize) {

Vector3f[] vertices = new Vector3f[heights.length];

for(int y = 0; y < mapsize; y++) {
for(int x = 0; x < mapsize; x++) {

int index = x + mapsize * y;
vertices[index] = new Vector3f(x, y, heights[index]);
}
}

// You should also set the indices manually when creating a custom Mesh.
// And that is my problem.
// I don't know how to determine the indexes of the triangles.
}
}
``````

I can simply set the vertices with a loop. But when you create a custom Mesh, you also have to set the indices of the triangles.
And that’s my problem. I don’t know how to set the indexes.

In the tutorial page “Connecting the Dots” is where I get stuck.

Which part is unclear?

You’ve created a big array of vertexes. Now you need to define what makes the triangles. What three vertexes make the first triangle? Those are the first three values in your index buffer.

I understand what the indexes are. But I can’t invent a way to set the indexes in a loop or something.
So I want to determine the indexes of the triangles when I have the heights array

Since your vertexes are in a grid then the math shouldn’t be too hard.

Iterate over the cells (y = 0; y < mapSize - 1) (x = 0; y < mapSize - 1)… then calculate the index of the base corner. Calculate the index of the other four corners. Add six indexes in the appropriate order.

I managed in getting the indexes at the right places.
Now I’m able to make my terrain visible, and so I did.
But, now I have another problem.
Here’s an image of the terrain: (top view)

Every time when I start the game and generate a terrain, there are some not-random heights like you see in the image. I always get an oblique line of low heights (the canyon) .
This is the tutorial I used: Realistic terrain in 130 lines
And the code of that tutorial: playfuljs-demos/index.html at gh-pages · hunterloftis/playfuljs-demos · GitHub

And here’s my own class that generates the terrain:

``````package terrain;

import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;

public class TerrainGenerator {

private static final int TERRAIN_WIDTH = 100;
private static final int TERRAIN_HEIGHT = 100;
private static final float DEFAULT_HEIGHT = 1.0f;
private static final int DETAIL = 6;
private static final int SIZE = (int) Math.pow(2, DETAIL) + 1;
private static final int MAX = SIZE - 1;
private static final float ROUGHNESS = 0.1f;
private float[] heights;
private AssetManager assetManager;

public TerrainGenerator(AssetManager assetManager) {

this.assetManager = assetManager;
}

public float get(int x, int y) {
try {
if (x < 0 || x > MAX || y < 0 || y > MAX) {
return -1f;
}

return heights[x + SIZE * y];
} catch (Exception ex) {

System.out.println("Error! > " + ex.getMessage());
}
return 0;
}

public void set(int x, int y, float val) {

heights[x + SIZE * y] = val;
}

public void initArray() {

heights = new float[SIZE * SIZE];

set(0, 0, MAX);
set(MAX, 0, MAX / 2);
set(MAX, MAX, 0);
set(0, MAX, MAX / 2);

System.out.println("Size of heights array: " + heights.length);
}

public void generateTerrain() {

initArray();
divide(MAX);

}

public void divide(float size) {

System.out.println("d " + size);
int x, y, half = (int) (size / 2);
float scale = ROUGHNESS * size;

if(half < 1f) {
return;
}

for (y = half; y < MAX; y += size) {
for (x = half; x < MAX; x += size) {

float random = (float) Math.random();
square(x, y, half, random * scale * 2 - scale);
}
}

for (y = 0; y <= MAX; y += half) {
for (x = (int) ((y + half) % size); x <= MAX; x += size) {

float random = (float) Math.random();
diamond(x, y, half, random * scale * 2 - scale);
}
}

divide(size / 2); // Cycle
}

public float average(float[] values) {

float[] valid = new float[values.length];

// check valid values
for (int i = 0; i < values.length; i++) {
if (values[i] != -1.0f) {
valid[i] = values[i];
}
}

// calculate average
float total = 0f;
for (int n = 0; n < valid.length; n++) {
total += valid[n];
}

}

public void square(int x, int y, int size, float offset) {

float ave = average(new float[]{
get(x - size, y - size), // upper left
get(x + size, y - size), // upper right
get(x + size, y + size), // lower right
get(x - size, y + size) // lower left
});

set(x, y, ave + offset);
}

public void diamond(int x, int y, int size, float offset) {

float ave = average(new float[]{
get(x, y - size), // top
get(x + size, y), // right
get(x, y + size), // bottom
get(x - size, y) // left
});

set(x, y, ave + offset);
}

public void debug() {

System.out.println("Open terrain debug window");

JFrame frm = new JFrame("Terrain debug map");
frm.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frm.setSize(TERRAIN_WIDTH * 10, TERRAIN_HEIGHT * 10);
frm.setLocationRelativeTo(null);

TerrainMapPanel panel = new TerrainMapPanel(heights, SIZE, MAX);
frm.setLayout(new BorderLayout());
frm.setVisible(true);

}

private Color getColor(int x, int y) {

return Color.red;
}

/**
* This method is only to get an object to render
*/
public Geometry getGeneratedTerrain() {

Terrain terr = new Terrain();
terr.setHeights(heights, MAX);

Geometry geom = new Geometry("terrain", terr);

Material mat_terrain = new Material(assetManager,
"Common/MatDefs/Terrain/Terrain.j3md");

"Textures/Terrain/splat/alphamap.png"));

"Textures/Terrain/splat/grass.png");
grass.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex1", grass);
mat_terrain.setFloat("Tex1Scale", 64f);

"Textures/Terrain/splat/dirt.png");
dirt.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex2", dirt);
mat_terrain.setFloat("Tex2Scale", 32f);

"Textures/Terrain/splat/rocks.png");
rock.setWrap(WrapMode.Repeat);
mat_terrain.setTexture("Tex3", rock);
mat_terrain.setFloat("Tex3Scale", 128f);

geom.setMaterial(mat_terrain);

geom.setLocalTranslation(-16f, -10f, 0);