I have taken the HelloTerrain file and changed it to load my own (1024 x 1024 pixel) height map. It worked but:
- it has seams that do not match each other. Four seams meet right under my starting location.
- The mountains look good, until I move, then they flatten out.
Then I added some collision detection from HelloCollision. My player falls until it lands in the middle of the seams. 3) Then, when I move, it falls through the seams!
Is there a fix for this? If the fix is in the nightly build, is there a Wiki page on how to set up jMP to use the svn copy instead of the realease copy?
Here is the code:
[java]package mygame;
import jme3tools.converters.ImageToAwt;
import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingBox;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
import com.jme3.bullet.nodes.PhysicsCharacterNode;
import com.jme3.bullet.nodes.PhysicsNode;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import java.util.ArrayList;
import java.util.List;
public class Main extends SimpleApplication implements ActionListener {
private TerrainQuad terrain;
private Material matRock;
private Material matWire;
private boolean wireframe = true;
protected BitmapText hintText;
private PointLight pl;
private Geometry lightMdl;
private BulletAppState bulletAppState;
private Spatial sceneModel;
private PhysicsNode landscape;
private PhysicsCharacterNode player;
private Vector3f walkDirection = new Vector3f();
private boolean left = false, right = false, up = false, down = false;
public static void main(String[] args) {
Main app = new Main();
app.start();
}
@Override
public void initialize() {
super.initialize();
loadHintText();
}
@Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
viewPort.setBackgroundColor(new ColorRGBA(0.7f,0.8f,1f,1f));
// We reuse the fly camera for rotation while position is handled by physics.
flyCam.setMoveSpeed(20);
setupKeys();
// First, we load up our textures and the heightmap texture for the terrain
// TERRAIN TEXTURE material
matRock = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
// ALPHA map (for splat textures)
matRock.setTexture("m_Alpha", assetManager.loadTexture("Textures/Terrain/splat/ireland-am.png"));
// HEIGHTMAP image (for the terrain heightmap)
Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/ireland-hm.png");
// DIRT texture
Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("m_Tex1", dirt);
matRock.setFloat("m_Tex1Scale", 32f);
// GRASS texture
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("m_Tex2", grass);
matRock.setFloat("m_Tex2Scale", 2048f);
// WATER texture
Texture water = assetManager.loadTexture("Textures/Terrain/splat/water.jpg");
water.setWrap(WrapMode.Repeat);
matRock.setTexture("m_Tex3", water);
matRock.setFloat("m_Tex3Scale", 8f);
// WIREFRAME material
matWire = new Material(assetManager, "Common/MatDefs/Misc/WireColor.j3md");
matWire.setColor("m_Color", ColorRGBA.Green);
// CREATE HEIGHTMAP
AbstractHeightMap heightmap = null;
try {
heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 2f);
//Try heightmap.load(false, false);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
/*
- Here we create the actual terrain. The tiles will be 65x65, and the total size of the
- terrain will be 513x513. It uses the heightmap we created to generate the height values.
*/
/**
- Optimal terrain patch size is 65 (64x64)
- If you go for small patch size, it will definitely slow down because the depth of
- the quad tree will increase, and more is done on the CPU then to traverse it.
- I plan to give each node in the tree a reference to its neighbours so that should
- resolve any of these slowdowns. -Brent
*
- The total size is up to you. At 1025 it ran fine for me (200+FPS), however at
- size=2049, it got really slow. But that is a jump from 2 million to 8 million triangles…
*/
terrain = new TerrainQuad("terrain", 65, 1025, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setModelBound(new BoundingBox());
terrain.updateModelBound();
terrain.setLocalTranslation(0, -1, 0);
terrain.setLocalScale(64f, 1f, 64f);
// We set up collision detection for the scene by creating a
// compound collision shape and a physics node.
// CompoundCollisionShape sceneShape =
// CollisionShapeFactory.createMeshCompoundShape(terrain);
HeightfieldCollisionShape sceneShape =
new HeightfieldCollisionShape (heightmap.getHeightMap());
landscape = new PhysicsNode(terrain, sceneShape, 0);
// We set up collision detection for the player by creating
// a capsule collision shape and a physics character node.
// The physics character node offers extra settings for
// size, stepheight, jumping, falling, and gravity.
// We also put the player in its starting position.
player = new PhysicsCharacterNode(new CapsuleCollisionShape(1.5f, 6f, 1), .05f);
player.setJumpSpeed(20);
player.setFallSpeed(30);
player.setGravity(30);
player.setLocalTranslation(new Vector3f(0, 500, 0));
// We attach the scene and the player to the rootnode and the physics space,
// to make them appear in the game world.
rootNode.attachChild(landscape);
rootNode.attachChild(player);
bulletAppState.getPhysicsSpace().add(landscape);
bulletAppState.getPhysicsSpace().add(player);
// rootNode.attachChild(terrain);
getCamera().getLocation().y = 500;
getCamera().setDirection(new Vector3f(0, 0, 0));
}
public void loadHintText() {
hintText = new BitmapText(guiFont, false);
hintText.setSize(guiFont.getCharSet().getRenderedSize());
hintText.setLocalTranslation(0, getCamera().getHeight(), 0);
hintText.setText("Hit T to switch to wireframe");
guiNode.attachChild(hintText);
}
private void setupKeys() {
flyCam.setMoveSpeed(50);
inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
inputManager.addListener(this, "wireframe");
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.addListener(this, "Lefts");
inputManager.addListener(this, "Rights");
inputManager.addListener(this, "Ups");
inputManager.addListener(this, "Downs");
inputManager.addListener(this, "Jumps");
}
public void onAction(String name, boolean pressed, float tpf) {
if (name.equals("wireframe") && !pressed) {
wireframe = !wireframe;
if (!wireframe) {
terrain.setMaterial(matWire);
} else {
terrain.setMaterial(matRock);
}
} else if (name.equals("Lefts")) {
if (pressed) { left = true; } else { left = false; }
} else if (name.equals("Rights")) {
if (pressed) { right = true; } else { right = false; }
} else if (name.equals("Ups")) {
if (pressed) { up = true; } else { up = false; }
} else if (name.equals("Downs")) {
if (pressed) { down = true; } else { down = false; }
} else if (name.equals("Jumps")) {
player.jump();
}
}
/**
- This is the main event loop–walking happens here.
- We check in which direction the player is walking by interpreting
- the camera direction forward (camDir) and to the side (camLeft).
- The setWalkDirection() command is what lets a physics-controlled player walk.
- We also make sure here that the camera moves with player.
*/
@Override
public void simpleUpdate(float tpf) {
Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
Vector3f camLeft = cam.getLeft().clone().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.getLocalTranslation());
}
}
[/java]