TerrainGrid (endless terrain) issues

Hello and Merry Xmas :slight_smile:



Im trying to implement the basic example for the endless terrain but I’m stuck on an issue that i cannot understand.



The error im getting in the console is:



SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.NullPointerException

at com.jme3.terrain.geomipmap.TerrainGrid.update(TerrainGrid.java:193)

at com.jme3.terrain.geomipmap.TerrainLodControl.controlUpdate(TerrainLodControl.java:125)

at com.jme3.terrain.geomipmap.TerrainLodControl.update(TerrainLodControl.java:100)

at com.jme3.scene.Spatial.runControlUpdate(Spatial.java:553)

at com.jme3.scene.Spatial.updateLogicalState(Spatial.java:671)

at com.jme3.scene.Node.updateLogicalState(Node.java:147)

at com.jme3.scene.Node.updateLogicalState(Node.java:154)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:259)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:149)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:223)

at java.lang.Thread.run(Thread.java:680)



Any help/hiints appreciated :slight_smile:



Thanks.

Merry Christmas!



Posting code would be helpful here…

Merry Christimas!



[java]at com.jme3.terrain.geomipmap.TerrainGrid.update(TerrainGrid.java:193)[/java]



Maybe looking at this help to solve the problem ;):



[java]

@Override

public void update(List<Vector3f> locations, LodCalculator lodCalculator) {

// for now, only the first camera is handled.

// to accept more, there are two ways:

// 1: every camera has an associated grid, then the location is not enough to identify which camera location has changed

// 2: grids are associated with locations, and no incremental update is done, we load new grids for new locations, and unload those that are not needed anymore

Vector3f cam = locations.isEmpty() ? Vector3f.ZERO.clone() : locations.get(0);

Vector3f camCell = this.getCamCell(cam); // get the grid index value of where the camera is (ie. 2,1)

if (cellsLoaded > 1) { // Check if cells are updated before updating gridoffset.

gridOffset[0] = Math.round(camCell.x * (size / 2));

gridOffset[1] = Math.round(camCell.z * (size / 2));

cellsLoaded = 0;

}

if (camCell.x != this.currentCamCell.x || camCell.z != currentCamCell.z || !runOnce) {

// if the camera has moved into a new cell, load new terrain into the visible 4 center quads

this.updateChildren(camCell);

for (TerrainGridListener l : this.listeners) {

l.gridMoved(camCell);

}

}

runOnce = true;

super.update(locations, lodCalculator);

}

[/java]

@sbook:

The code is taken from here :



http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/terrain/TerrainGridTest.java



I only changed the class name to Main .



At the moment, i passed that issue , by using the libraries from the nightly builds instead of the default libraries that jme platform beta comes with. Still, i have no terrain showing. Im thinking it has to do with changes in the TerrainGrid…but as im a total newbie in jme and haven’t been in touch with java for some years, thats all i can think of :slight_smile:



@ glaucomardano: Thanks. Indeed i managed to traced it all the way to that file also, its at line 14 (14 from your code snippet) that this happened. As i said, seems to be working now if i dont use the standard libs but the nightly build ones, but i dont see anything , just the background, although if i move with WASD, the tiles are changing (i can see it in the netbeans console).

Move down, you are just too high to see the terrain.

1 Like

@normen I’ll try again to move down but …how much down? :slight_smile: i kept moving down for 4-5 seconds, nothing …



Maybe it has something to do with this?



Dec 25, 2011 8:30:19 PM com.jme3.renderer.lwjgl.LwjglRenderer updateUniformLocation

INFO: Uniform m_VertexColor is not declared in shader [ShaderSource[name=Common/MatDefs/Misc/Unshaded.vert, defines, type=Vertex], ShaderSource[name=Common/MatDefs/Misc/Unshaded.frag, defines, type=Fragment]].

No, it hasn’t

Well, thanks anyway normen. Maybe im just missing something, dont know :slight_smile:

Did you change the code at all from TerrainGridTest.java?

In the latest release of jme (the nightly build from Trunk), like 193 (where you error is happening) does not exist. So you can try checking out the latest version and see if it is fixed there.

@Sploreg



I got the code from here: http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/terrain/TerrainGridTest.java .



The only thing i changed is the name of the class since all the code is in Main.java, and the name of the package (first line in the file) . Nothing else was changed.

[java]

package mygame;



import com.jme3.app.SimpleApplication;

import com.jme3.app.state.ScreenshotAppState;

import com.jme3.asset.plugins.HttpZipLocator;

import com.jme3.asset.plugins.ZipLocator;

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.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.light.DirectionalLight;

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.TerrainGridListener;

import com.jme3.terrain.geomipmap.TerrainLodControl;

import com.jme3.terrain.geomipmap.TerrainQuad;

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

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

import com.jme3.terrain.heightmap.Namer;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.WrapMode;

import java.io.File;



public class Main extends SimpleApplication {



private Material mat_terrain;

private TerrainGrid terrain;

private float grassScale = 64;

private float dirtScale = 16;

private float rockScale = 128;

private boolean usePhysics = false;

private boolean physicsAdded = false;



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

Main app = new Main();

app.start();

}

private CharacterControl player3;



@Override

public void simpleInitApp() {

File file = new File(“TerrainGridTestData.zip”);

if (!file.exists()) {

assetManager.registerLocator(“http://jmonkeyengine.googlecode.com/files/TerrainGridTestData.zip”, HttpZipLocator.class);

} else {

assetManager.registerLocator(“TerrainGridTestData.zip”, ZipLocator.class);

}



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 :slight_smile:

// 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(88, 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, 90, 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”, 129);



this.terrain = new TerrainGrid(“terrain”, 65, 257, new ImageTileLoader(assetManager, new Namer() {



public String getName(int x, int y) {

return “Scenes/TerrainMountains/terrain_” + x + “_” + y + “.png”;

}

}));



this.terrain.setMaterial(mat_terrain);

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

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

this.rootNode.attachChild(this.terrain);



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

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

this.terrain.addControl(control);



final BulletAppState bulletAppState = new BulletAppState();

stateManager.attach(bulletAppState);



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



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



DirectionalLight light = new DirectionalLight();

light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize());

rootNode.addLight(light);



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 Material tileLoaded(Material material, Vector3f cell) {

return material;

}



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) {

bulletAppState.getPhysicsSpace().remove(quad);

quad.removeControl(RigidBodyControl.class);

}



});

}



this.initKeys();

}



private void initKeys() {

// You can map one or several inputs to one named action

this.inputManager.addMapping(“Lefts”, new KeyTrigger(KeyInput.KEY_A));

this.inputManager.addMapping(“Rights”, new KeyTrigger(KeyInput.KEY_D));

this.inputManager.addMapping(“Ups”, new KeyTrigger(KeyInput.KEY_W));

this.inputManager.addMapping(“Downs”, new KeyTrigger(KeyInput.KEY_S));

this.inputManager.addMapping(“Jumps”, new KeyTrigger(KeyInput.KEY_SPACE));

this.inputManager.addListener(this.actionListener, “Lefts”);

this.inputManager.addListener(this.actionListener, “Rights”);

this.inputManager.addListener(this.actionListener, “Ups”);

this.inputManager.addListener(this.actionListener, “Downs”);

this.inputManager.addListener(this.actionListener, “Jumps”);

}

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();

}

}

};

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());

}

}

}

[/java]