TerrainGeneration Issues

I’m trying to generate a terrain, it works well, but when I’m above the terrain it becomes partly transparent, I have tried many things but nothing worked out, any ideas?

P.S Looks like I’ve found out what’s wrong with that, it generates upside down, but I have no idea why and how to fix that

import com.jme3.asset.AssetManager;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.texture.Texture;
import com.jme3.util.BufferUtils;
import com.sudoplay.joise.module.ModuleBasisFunction;
import com.sudoplay.joise.module.ModuleFractal;

public class TerrainGenerator {

    private final AssetManager assetManager;

    public TerrainGenerator(AssetManager assetManager) {
        this.assetManager = assetManager;
    }

    public TerrainAndMaterial generateTerrain(int gridSize, float stepSize, float frequency, int numOctaves) {
        // Create geometry for the terrain
        Mesh terrainMesh = generateTerrainMesh(gridSize, stepSize, frequency, numOctaves);
        Geometry terrainGeometry = new Geometry("Terrain", terrainMesh);

        // Create material for the terrain
        Material terrainMaterial = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
        Texture diffuseTexture = assetManager.loadTexture("assets/textures/grass.jpg"); // Replace with your texture file
        diffuseTexture.setWrap(Texture.WrapMode.Repeat);
        terrainMaterial.setTexture("DiffuseMap", diffuseTexture);

        terrainMaterial.setBoolean("UseMaterialColors", true);
        terrainMaterial.setColor("Ambient", ColorRGBA.Gray);
        terrainMaterial.setColor("Diffuse", ColorRGBA.White);

        terrainGeometry.setMaterial(terrainMaterial);

        // Add directional light
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
        sun.setColor(ColorRGBA.White);

        terrainGeometry.addLight(sun);

        return new TerrainAndMaterial(terrainGeometry, terrainMaterial);
    }

    private Mesh generateTerrainMesh(int gridSize, float stepSize, float frequency, int numOctaves) {
        // Create mesh and configure its vertices and triangle indices
        Mesh terrainMesh = new Mesh();
        Vector3f[] vertices = new Vector3f[(gridSize + 1) * (gridSize + 1)];
        Vector2f[] texCoord = new Vector2f[(gridSize + 1) * (gridSize + 1)]; // Add array for texture coordinates

        // Create noise module for height generation
        ModuleFractal fractal = new ModuleFractal();
        fractal.setAllSourceTypes(ModuleBasisFunction.BasisType.GRADIENT, ModuleBasisFunction.InterpolationType.LINEAR);
        fractal.setNumOctaves(numOctaves);
        fractal.setFrequency(frequency);

        for (int i = 0; i <= gridSize; i++) {
            for (int j = 0; j <= gridSize; j++) {
                float x = -gridSize * stepSize * 0.5f + i * stepSize;
                float z = -gridSize * stepSize * 0.5f + j * stepSize;

                // Generate noise for current position (x, z)
                float noiseValue = (float) fractal.get(x, 0, z);

                // Create additional noise module for hill generation
                ModuleFractal hillFractal = new ModuleFractal();
                hillFractal.setAllSourceTypes(ModuleBasisFunction.BasisType.GRADIENT, ModuleBasisFunction.InterpolationType.LINEAR);
                hillFractal.setNumOctaves(4);
                hillFractal.setFrequency(0.1f);

                // Generate hill height for current position (x, z)
                float hillHeight = (float) hillFractal.get(x * 0.2, 0, z * 0.2) * 10;

                float y = noiseValue * 15 + hillHeight;

                vertices[i * (gridSize + 1) + j] = new Vector3f(x, y, z);

                // Generate texture coordinates in range [0, 1]
                float u = (float) i / gridSize;
                float v = (float) j / gridSize;
                texCoord[i * (gridSize + 1) + j] = new Vector2f(u, v);
            }
        }

        // Create triangle indices
        int[] indices = new int[gridSize * gridSize * 6];
        int idx = 0;
        for (int i = 0; i < gridSize; i++) {
            for (int j = 0; j < gridSize; j++) {
                int topLeft = i * (gridSize + 1) + j;
                int bottomLeft = (i + 1) * (gridSize + 1) + j;

                // Triangle 1 vertices
                indices[idx++] = topLeft;
                indices[idx++] = bottomLeft;
                indices[idx++] = topLeft + 1;

                // Triangle 2 vertices
                indices[idx++] = topLeft + 1;
                indices[idx++] = bottomLeft;
                indices[idx++] = bottomLeft + 1;
            }
        }

        // Set vertices, texture coordinates, and indices to the mesh
        terrainMesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
        terrainMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
        terrainMesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(indices));

        // Calculate normals for lighting
        terrainMesh.updateBound();
        terrainMesh.updateCounts();

        return terrainMesh;
    }

    public class TerrainAndMaterial {
        public final Geometry terrainGeometry;
        public final Material terrainMaterial;

        public TerrainAndMaterial(Geometry terrainGeometry, Material terrainMaterial) {
            this.terrainGeometry = terrainGeometry;
            this.terrainMaterial = terrainMaterial;
        }
    }
}

That’s what I see from the bottom

I’ve figured that out, the problem was in triangle indices
Here’s an improved code

int[] indices = new int[gridSize * gridSize * 6];
    int idx = 0;
    for (int i = 0; i < gridSize; i++) {
        for (int j = 0; j < gridSize; j++) {
            int topLeft = i * (gridSize + 1) + j;
            int bottomLeft = (i + 1) * (gridSize + 1) + j;

            // Triangle 1 vertices (reversed order)
            indices[idx++] = topLeft + 1;
            indices[idx++] = bottomLeft;
            indices[idx++] = topLeft;

            // Triangle 2 vertices (reversed order)
            indices[idx++] = bottomLeft + 1;
            indices[idx++] = bottomLeft;
            indices[idx++] = topLeft + 1;
        }
    }`

JMonkeyEngine follows the convention that visible triangles have right-handed/counter-clockwise winding.

You can override this convention by setting the FaceCullMode in the additional render state (of the Material of the Geometry containing your custom mesh) to Front or Off:

      geometry.getMaterial()
                .getAdditionalRenderState()
                .setFaceCullMode(RenderState.FaceCullMode.Front);

You might want to re-read the wiki page on custom meshes:

1 Like