TerrainGrid / Terrain Collision

My player falls through the terrain. The terrain should be solid so I don’t know what to do.
The terrain is an TerrainGrid. Can somebody help me?

package jme3test.terrain;



import com.jme3.app.SimpleApplication;

import com.jme3.app.state.ScreenshotAppState;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;


import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.terrain.geomipmap.TerrainGrid;

import com.jme3.terrain.geomipmap.TerrainGridLodControl;

import com.jme3.terrain.geomipmap.TerrainLodControl;

import com.jme3.terrain.geomipmap.grid.FractalTileLoader;

import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;

import com.jme3.terrain.noise.ShaderUtils;

import com.jme3.terrain.noise.basis.FilteredBasis;

import com.jme3.terrain.noise.filter.IterativeFilter;

import com.jme3.terrain.noise.filter.OptimizedErode;

import com.jme3.terrain.noise.filter.PerturbFilter;

import com.jme3.terrain.noise.filter.SmoothFilter;

import com.jme3.terrain.noise.fractal.FractalSum;

import com.jme3.terrain.noise.modulator.NoiseModulator;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.WrapMode;



public class TerrainFractalGridTest extends SimpleApplication implements ActionListener{



    private Material mat_terrain;

    private TerrainGrid terrain;

    private float grassScale = 64;

    private float dirtScale = 16;

    private float rockScale = 128;
    
    private Vector3f walkDirection = new Vector3f();
    private boolean left = false;
    private boolean right = false;  
    private boolean up = false;  
    private boolean down = false;  
        
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();
    
    private FractalSum base;

    private PerturbFilter perturb;

    private OptimizedErode therm;

    private SmoothFilter smooth;

    private IterativeFilter iterate;
    
    private BulletAppState bulletAppState;
    private RigidBodyControl landscape;
    private CharacterControl player;



    public static void main(final String[] args) {

        TerrainFractalGridTest app = new TerrainFractalGridTest();

        app.start();

    }

    private CharacterControl player3;

    



    @Override

    public void simpleInitApp() {

        this.flyCam.setMoveSpeed(100f);

        ScreenshotAppState state = new ScreenshotAppState();

        this.stateManager.attach(state);



        // TERRAIN TEXTURE material

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



        // Parameters to material:

        // regionXColorMap: X = 1..4 the texture that should be appliad to state X

        // regionX: a Vector3f containing the following information:

        //      regionX.x: the start height of the region

        //      regionX.y: the end height of the region

        //      regionX.z: the texture scale for the region

        //  it might not be the most elegant way for storing these 3 values, but it packs the data nicely :)

        // slopeColorMap: the texture to be used for cliffs, and steep mountain sites

        // slopeTileFactor: the texture scale for slopes

        // terrainSize: the total size of the terrain (used for scaling the texture)

        // GRASS texture

        Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");

        grass.setWrap(WrapMode.Repeat);

        this.mat_terrain.setTexture("region1ColorMap", grass);

        this.mat_terrain.setVector3("region1", new Vector3f(15, 200, this.grassScale));



        // DIRT texture

        Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");

        dirt.setWrap(WrapMode.Repeat);

        this.mat_terrain.setTexture("region2ColorMap", dirt);

        this.mat_terrain.setVector3("region2", new Vector3f(0, 20, this.dirtScale));



        // ROCK texture

        Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg");

        rock.setWrap(WrapMode.Repeat);

        this.mat_terrain.setTexture("region3ColorMap", rock);

        this.mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale));



        this.mat_terrain.setTexture("region4ColorMap", rock);

        this.mat_terrain.setVector3("region4", new Vector3f(198, 260, this.rockScale));



        this.mat_terrain.setTexture("slopeColorMap", rock);

        this.mat_terrain.setFloat("slopeTileFactor", 32);



        this.mat_terrain.setFloat("terrainSize", 1025);



        this.base = new FractalSum();

        this.base.setRoughness(0.7f);

        this.base.setFrequency(1.0f);

        this.base.setAmplitude(1.0f);

        this.base.setLacunarity(2.12f);

        this.base.setOctaves(8);

        this.base.setScale(0.02125f);

        this.base.addModulator(new NoiseModulator() {



            @Override

            public float value(float... in) {

                return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);

            }

        });



        FilteredBasis ground = new FilteredBasis(this.base);



        this.perturb = new PerturbFilter();

        this.perturb.setMagnitude(0.119f);



        this.therm = new OptimizedErode();

        this.therm.setRadius(5);

        this.therm.setTalus(0.011f);



        this.smooth = new SmoothFilter();

        this.smooth.setRadius(1);

        this.smooth.setEffect(0.7f);



        this.iterate = new IterativeFilter();

        this.iterate.addPreFilter(this.perturb);

        this.iterate.addPostFilter(this.smooth);

        this.iterate.setFilter(this.therm);

        this.iterate.setIterations(1);



        ground.addPreFilter(this.iterate);



        this.terrain = new TerrainGrid("terrain", 33, 129, new FractalTileLoader(ground, 128f));



        this.terrain.setMaterial(this.mat_terrain);

        this.terrain.setLocalTranslation(0, 0, 0);

        this.terrain.setLocalScale(2f, 1f, 2f);

        this.rootNode.attachChild(this.terrain);



        TerrainLodControl control = new TerrainGridLodControl(this.terrain, this.getCamera());

        control.setLodCalculator(new DistanceLodCalculator(33, 2.7f)); // patch size, and a multiplier

        this.terrain.addControl(control);







        this.getCamera().setLocation(new Vector3f(0, 300, 0));



        this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));

        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        CollisionShape sceneShape = CollisionShapeFactory.createDynamicMeshShape(terrain);
        landscape = new RigidBodyControl(sceneShape, 0);
        terrain.addControl(landscape);
        //terrain.addControl(new RigidBodyControl(1));
        CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
        player = new CharacterControl(capsuleShape, 0.05f);
        player.setJumpSpeed(20);
        player.setFallSpeed(10);
        player.setGravity(new Vector3f(0,-50f,0));
        player.setPhysicsLocation(new Vector3f(0, 100, 0));
        //bulletAppState.getPhysicsSpace().add(landscape);
        bulletAppState.getPhysicsSpace().add(player);
        bulletAppState.getPhysicsSpace().add(terrain);
        player.setUpAxis(1);
        System.out.println(player.isEnabled());
        
        setUpKeys();
    }
    
    private void setUpKeys() {
            inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
            inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
            inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
            inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
            inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
            inputManager.addListener(this, "Left");
            inputManager.addListener(this, "Right");
            inputManager.addListener(this, "Up");
            inputManager.addListener(this, "Down");
            inputManager.addListener(this, "Jump");
    }
    
    public void onAction(String binding, boolean isPressed, float tpf) {
    if (binding.equals("Left")) {
      left = isPressed;
    } else if (binding.equals("Right")) {
      right= isPressed;
    } else if (binding.equals("Up")) {
      up = isPressed;
    } else if (binding.equals("Down")) {
      down = isPressed;
    } else if (binding.equals("Jump")) {
      // pre jME3.2
      //if (isPressed) { player.jump();}

      // >= jME3.2
      player.jump(new Vector3f(0,20f,0));
    }
    }


    @Override

    public void simpleUpdate(final float tpf) {
        cam.setLocation(player.getPhysicsLocation());
        
        camDir.set(cam.getDirection()).multLocal(0.6f);
        camLeft.set(cam.getLeft()).multLocal(0.4f);
        walkDirection.set(0, 0, 0);
        if (left) {
            walkDirection.addLocal(camLeft);
        }
        if (right) {
            walkDirection.addLocal(camLeft.negate());
        }
        if (up) {
            walkDirection.addLocal(camDir);
        }
        if (down) {
            walkDirection.addLocal(camDir.negate());
        }
        player.setWalkDirection(walkDirection);
        cam.setLocation(player.getPhysicsLocation());
    }

}

I used the file TerrainFractalGridTest.java from the tutorial.

I added these fields:

private BulletAppState bulletAppState;
private RigidBodyControl landscape;
private CharacterControl player;

and this code:

 bulletAppState = new BulletAppState();
 stateManager.attach(bulletAppState);
 CollisionShape sceneShape = CollisionShapeFactory.createDynamicMeshShape(terrain);
 landscape = new RigidBodyControl(sceneShape, 0);
 terrain.addControl(landscape);
 //terrain.addControl(new RigidBodyControl(1));
 CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
 player = new CharacterControl(capsuleShape, 0.05f);
 player.setJumpSpeed(20);
 player.setFallSpeed(10);
 player.setGravity(new Vector3f(0,-50f,0));
 player.setPhysicsLocation(new Vector3f(0, 100, 0));
 //bulletAppState.getPhysicsSpace().add(landscape);
 bulletAppState.getPhysicsSpace().add(player);
 bulletAppState.getPhysicsSpace().add(terrain);
 player.setUpAxis(1);
 System.out.println(player.isEnabled());

im quite certain terrain uses a HeightfieldCollisionShape.

Can you tell me more about how to use it in the code?

https://wiki.jmonkeyengine.org/jme3/advanced/physics.html#create-a-collisionshape

https://wiki.jmonkeyengine.org/jme3/beginner/hello_terrain.html#what-is-a-heightmap

Deprecated. Look at Design Question - Terrain instead.

https://wiki.jmonkeyengine.org/jme3/advanced/terrain.html