Terrain Grid Usage

I’ve been having trouble with getting the LODs and the tile loading functionality for the terrain grid working. I’m not sure if im using the right code, or if a terrain grid is even my best option; my game has one large map that’s made up of multiple scenes I made in the terrain editor and scene composer.

        tile1 = (Node)app.getAssetManager().loadModel("Scenes/startZone.j3o");
        
        grid = new TerrainGrid("terrain", 65, 257, new AssetTileLoader());
        grid.attachChild(tile1);
        grid.getGridTileLoader().setPatchSize(20);

       TerrainGridLodControl lodControl = new TerrainGridLodControl(grid, app.getCamera());
       lodControl.setLodCalculator( new DistanceLodCalculator(20, 2.7f) );
       grid.addControl(lodControl);

Any help or advice would be appreciated, I’ve only recently started working on optimizing the models and scenes for my world and just noticed how much memory a large terrain can take up

1 Like

TerrainGrid examples provided by the SDK do not answer your questions?

1 Like

I’ve found and tried multiple examples including what I saw in the tutorials but couldn’t get them to work unfortunately :confused: for some reason I could not find a single example that uses LODs with a TerrainGrid, only TerrainQuads. Unless I’ve somehow overlooked the right documentation. I’ve looked at the terra monkey and terrain grid tutorials, the javadoc for the terraingrtid http://javadoc.jmonkeyengine.org/com/jme3/terrain/geomipmap/TerrainGrid.html, multiple forum posts although most looked outdated and I think I saw someone mention that its depricated?, and I’ve also tried the above code with all 3 different Tile Loader types, and I couldn’t find much documentation on those unless I didn’t look in the right place.

For some reason i feel like I’m doing this wrong by using a Node instead of a Terrain, but I’ve also tried using tile1.getChild(“terrain”) and adding the LOD control directly to the terrain without any success. That’s partially why I also asked if this is even the best way to combine multiple scene nodes to reduce the memory or if I’ll have to resort to writing my own way of loading up only nearby objects and terrain

1 Like

It may seem strange but this example works.

package test;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.grid.AssetTileLoader;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;


public class Main extends SimpleApplication {

    private TerrainGrid terrain;
    private boolean usePhysics = true;

    public static void main(final String[] args) {
        Main app = new Main();
        app.start();
    }
    private CharacterControl player3;
	private Material matWire;

    @Override
    public void simpleInitApp() {

        flyCam.setMoveSpeed(100f);
        
        AssetTileLoader grid = new AssetTileLoader(assetManager, "testgrid", "TerrainGrid");
        
        terrain = new TerrainGrid("terrain", 65, 257, grid);
        
        matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        matWire.getAdditionalRenderState().setWireframe(true);
        terrain.setMaterial(matWire);
        
        rootNode.attachChild(terrain);
        
        TerrainLodControl control = new TerrainGridLodControl(terrain, getCamera());
        control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) );
        terrain.addControl(control);
        
        final BulletAppState bulletAppState = new BulletAppState();
        
        stateManager.attach(bulletAppState);

        getCamera().setLocation(new Vector3f(0, 256, 0));

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

        if (usePhysics) {
            CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
            player3 = new CharacterControl(capsuleShape, 0.5f);
            player3.setJumpSpeed(20);
            player3.setFallSpeed(10);
            player3.setGravity(10);

            player3.setPhysicsLocation(new Vector3f(cam.getLocation().x, 256, cam.getLocation().z));

            bulletAppState.getPhysicsSpace().add(player3);

            terrain.addListener(new TerrainGridListener() {

                public void gridMoved(Vector3f newCenter) {
                }

                public void tileAttached(Vector3f cell, TerrainQuad quad) {
                    while(quad.getControl(RigidBodyControl.class)!=null){
                        quad.removeControl(RigidBodyControl.class);
                    }
                    quad.addControl(new RigidBodyControl(new HeightfieldCollisionShape(quad.getHeightMap(), terrain.getLocalScale()), 0));
                    bulletAppState.getPhysicsSpace().add(quad);
                }

                public void tileDetached(Vector3f cell, TerrainQuad quad) {
                    if (quad.getControl(RigidBodyControl.class) != null) {
                        bulletAppState.getPhysicsSpace().remove(quad);
                        quad.removeControl(RigidBodyControl.class);
                    }
                }

            });
        }
        
        this.initKeys();
    }

    private void initKeys() {
        // You can map one or several inputs to one named action
        inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("Jumps", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping("pick", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addListener(actionListener, "Lefts");
        inputManager.addListener(actionListener, "Rights");
        inputManager.addListener(actionListener, "Ups");
        inputManager.addListener(actionListener, "Downs");
        inputManager.addListener(actionListener, "Jumps");
        inputManager.addListener(actionListener, "pick");
    }
    private boolean left;
    private boolean right;
    private boolean up;
    private boolean down;
    
    private final ActionListener actionListener = new ActionListener() {

        @Override
        public void onAction(final String name, final boolean keyPressed, final float tpf) {
            if (name.equals("Lefts")) {
                if (keyPressed) {
                    Main.this.left = true;
                } else {
                    Main.this.left = false;
                }
            } else if (name.equals("Rights")) {
                if (keyPressed) {
                    Main.this.right = true;
                } else {
                    Main.this.right = false;
                }
            } else if (name.equals("Ups")) {
                if (keyPressed) {
                    Main.this.up = true;
                } else {
                    Main.this.up = false;
                }
            } else if (name.equals("Downs")) {
                if (keyPressed) {
                    Main.this.down = true;
                } else {
                    Main.this.down = false;
                }
            } else if (name.equals("Jumps")) {
                Main.this.player3.jump();
            } else if (name.equals("pick") && keyPressed) {
                //Terrain picked = terrain.getTerrainAt(player3.getPhysicsLocation());
                Terrain picked = terrain.getTerrainAtCell(terrain.getCurrentCell());
                System.out.println("** cell "+player3.getPhysicsLocation()+" picked terrain: "+picked);
            }
        }
    };
    private final Vector3f walkDirection = new Vector3f();

    @Override
    public void simpleUpdate(final float tpf) {
        Vector3f camDir = this.cam.getDirection().clone().multLocal(0.6f);
        Vector3f camLeft = this.cam.getLeft().clone().multLocal(0.4f);
        this.walkDirection.set(0, 0, 0);
        if (this.left) {
            this.walkDirection.addLocal(camLeft);
        }
        if (this.right) {
            this.walkDirection.addLocal(camLeft.negate());
        }
        if (this.up) {
            this.walkDirection.addLocal(camDir);
        }
        if (this.down) {
            this.walkDirection.addLocal(camDir.negate());
        }

        if (usePhysics) {
            this.player3.setWalkDirection(this.walkDirection);
            this.cam.setLocation(this.player3.getPhysicsLocation());
        }
    }
}

I could not install setWireframe, so j3o has its own stuff. But in this example, the LOD does the work.

1 Like

For terraingrid style worlds i wrote something a while ago. It is a custom setup but i memory serves had more versatility, including lod controls.

I also began to write some texture array terrain materials too. I have worked more on this but havent committed the code. Ill push it tomorrow.

1 Like

I went ahead and updated the Endless Terrain wiki page.

The TerrainGridTest.java has an example for you.

The google code link will not work so you have to download theTerrainGridTestData.zip file here,
https://code.google.com/archive/p/jmonkeyengine/downloads

and use a zip locator to run it,
https://jmonkeyengine.github.io/wiki/jme3/advanced/asset_manager.html#example-code-loading-assets

See if that helps.

Edit:Jayfella post hit before mine but his solution is basically recommended if you follow the link on deprecation.

1 Like

He seemed to be talking about j3o.

1 Like

True.

I took this to mean exactly what it says. Which was true.

Since both of you are using the same code from the this wiki page I updated it. That’s what my post is about.

1 Like

This is an example with a PNG-based.

1 Like

Minimum working example.

package test;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.material.Material;
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.AssetTileLoader;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;

public class Main extends SimpleApplication {

    private TerrainGrid terrain;

    public static void main(final String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {

        flyCam.setMoveSpeed(100f);
        getCamera().setLocation(new Vector3f(0, 256, 0));
        
        assetManager.registerLocator("res", FileLocator.class);
        
        AssetTileLoader grid = new AssetTileLoader(assetManager, "testgrid", "MyTerrainGrid");
        
        terrain = new TerrainGrid("terrain", 65, 257, grid);

        Material mat_terrain = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat_terrain.getAdditionalRenderState().setWireframe(true);
        terrain.setMaterial(mat_terrain);

        rootNode.attachChild(terrain);
        
        TerrainLodControl control = new TerrainGridLodControl(terrain, getCamera());
        control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) );
        terrain.addControl(control);


    }

}

Create a directory: res / MyTerrainGrid
And place 2 j3o files with names

Testgrid_0_0_0.j3o
Testgrid_0_0_1.j3o

You can add more as needed. Template: Testgrid_x_y_z.j3o

1 Like

I updated the LOD wiki page also. Just broken links and formatting for today.

3 Likes

Nice I’ll have to give terrain world a try as well and see about using that especially if that’s a better alternative to a terrain grid. I just downloaded it but I don’t see any jars to import in the download folders so does that mean i just need to build the project and import the jars to my game then I’m good to go?

@AdiDOS I also just gave that a try and it doesn’t seem to work for me still, I feel like I must just be doing something wrong and have absolutely no clue, it looks like a small active patch is showing up but it’s entirely white and none of the models I placed in the scene composer show up . It looks like I did everything right, placed my scenes in the folder: “Assets/res/MyTerrainGrid/Testgrid_0_0_0.j3o”, and I copied that code exactly. I also noticed that even though some blank terrain is rendering, when I use terrainGrid.getChildren().size() it shows that my terrainGrid has no children.

1 Like

I finished fixing broken links that were linked to the terrain pages.

The wiki is massive and contains hundreds of pages. If you ever run into broken links or pages in need of updating its better to say something on the forum or post it on github.

1 Like

Neither the name of ‘jMonkeyEngine’ nor the names of its contributors

  • may be used to endorse or promote products derived from this software
  • without specific prior written permission.
1 Like

There is one nuance that is a product and how it relates to software.

1 Like

Your using the jmonkeyengine name in the link, that is promoting. Quit trying to split hairs, you know what your doing is not allowed.

1 Like

I’m promoting the file?

1 Like

Its really time for that url to be added to a spam filter for this forum IMO.

1 Like

Can you refer to anything when talking about spam?

1 Like

If it were up to me you’d be already banned.

1 Like