TerrainGrid added to terrain package, test included
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7483 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
216
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.jme3.terrain.geomipmap;
|
||||
|
||||
import com.jme3.bullet.PhysicsSpace;
|
||||
import com.jme3.bullet.collision.shapes.HeightfieldCollisionShape;
|
||||
import com.jme3.bullet.control.RigidBodyControl;
|
||||
import com.jme3.terrain.heightmap.HeightMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory;
|
||||
import com.jme3.terrain.geomipmap.lodcalc.LodDistanceCalculatorFactory;
|
||||
import com.jme3.terrain.heightmap.HeightMapGrid;
|
||||
|
||||
/**
|
||||
* @author Anthyon
|
||||
*/
|
||||
public class TerrainGrid extends TerrainQuad {
|
||||
|
||||
private static Logger log = Logger.getLogger(TerrainGrid.class.getCanonicalName());
|
||||
private Vector3f currentCell;
|
||||
private int quarterSize;
|
||||
private int quadSize;
|
||||
private HeightMapGrid heightMapGrid;
|
||||
private Vector3f[] quadOrigins;
|
||||
private Vector3f[] quadIndex;
|
||||
private Map<String, TerrainGridListener> listeners = new HashMap<String, TerrainGridListener>();
|
||||
private Material material;
|
||||
|
||||
public TerrainGrid(String name, int patchSize, int size, Vector3f stepScale, HeightMapGrid heightMapGrid, int totalSize,
|
||||
Vector2f offset, float offsetAmount, LodCalculatorFactory lodCalculatorFactory) {
|
||||
this.name = name;
|
||||
this.patchSize = patchSize;
|
||||
this.size = size;
|
||||
this.quarterSize = size >> 2;
|
||||
this.quadSize = (size + 1) >> 1;
|
||||
this.stepScale = stepScale;
|
||||
this.heightMapGrid = heightMapGrid;
|
||||
heightMapGrid.setSize(this.quadSize);
|
||||
this.totalSize = totalSize;
|
||||
this.offset = offset;
|
||||
this.offsetAmount = offsetAmount;
|
||||
this.lodCalculatorFactory = lodCalculatorFactory;
|
||||
if (lodCalculatorFactory == null) {
|
||||
lodCalculatorFactory = new LodDistanceCalculatorFactory();
|
||||
}
|
||||
this.quadOrigins = new Vector3f[]{new Vector3f(-this.quarterSize, 0, -this.quarterSize).mult(this.stepScale),
|
||||
new Vector3f(-this.quarterSize, 0, this.quarterSize).mult(this.stepScale),
|
||||
new Vector3f(this.quarterSize, 0, -this.quarterSize).mult(this.stepScale),
|
||||
new Vector3f(this.quarterSize, 0, this.quarterSize).mult(this.stepScale)};
|
||||
this.quadIndex = new Vector3f[]{new Vector3f(0, 0, 0), new Vector3f(0, 0, 1), new Vector3f(1, 0, 0), new Vector3f(1, 0, 1)};
|
||||
|
||||
updateChildrens(Vector3f.ZERO);
|
||||
}
|
||||
|
||||
public TerrainGrid(String name, int patchSize, int size, Vector3f scale, HeightMapGrid heightMapGrid,
|
||||
LodCalculatorFactory lodCalculatorFactory) {
|
||||
this(name, patchSize, size, scale, heightMapGrid, size, new Vector2f(), 0, lodCalculatorFactory);
|
||||
}
|
||||
|
||||
public TerrainGrid(String name, int patchSize, int totalSize, HeightMapGrid heightMapGrid, LodCalculatorFactory lodCalculatorFactory) {
|
||||
this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMapGrid, lodCalculatorFactory);
|
||||
}
|
||||
|
||||
public TerrainGrid(String name, int patchSize, int totalSize, HeightMapGrid heightMapGrid) {
|
||||
this(name, patchSize, totalSize, heightMapGrid, null);
|
||||
}
|
||||
|
||||
public TerrainGrid() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(List<Vector3f> locations) {
|
||||
// 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.get(0);
|
||||
Vector3f camCell = this.getCell(cam);
|
||||
if (!camCell.equals(this.currentCell)) {
|
||||
this.updateChildrens(camCell);
|
||||
for (TerrainGridListener l : this.listeners.values()) {
|
||||
l.gridMoved(camCell);
|
||||
}
|
||||
}
|
||||
|
||||
super.update(locations);
|
||||
}
|
||||
|
||||
public Vector3f getCell(Vector3f location) {
|
||||
final Vector3f v = location.clone().divideLocal(this.getLocalScale().mult(this.quadSize)).add(0.5f, 0, 0.5f);
|
||||
return new Vector3f(FastMath.floor(v.x), FastMath.floor(v.y), FastMath.floor(v.z));
|
||||
}
|
||||
|
||||
protected void removeQuad(int idx) {
|
||||
this.detachChild(this.getQuad(idx));
|
||||
}
|
||||
|
||||
protected void moveQuad(int from, int to) {
|
||||
this.removeQuad(to);
|
||||
TerrainQuad fq = this.getQuad(from);
|
||||
fq.setQuadrant((short) to);
|
||||
fq.setLocalTranslation(this.quadOrigins[to - 1]);
|
||||
}
|
||||
|
||||
protected TerrainQuad createQuadAt(Vector3f location, int quadrant) {
|
||||
final HeightMap heightMapAt = this.heightMapGrid.getHeightMapAt(location);
|
||||
TerrainQuad q = new TerrainQuad(this.getName() + "Quad" + location, this.patchSize, this.quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap(), this.lodCalculatorFactory);
|
||||
q.setLocalTranslation(this.quadOrigins[quadrant - 1]);
|
||||
q.setMaterial(this.material);
|
||||
q.setQuadrant((short) quadrant);
|
||||
return q;
|
||||
}
|
||||
|
||||
private void updateChildrens(Vector3f cam) {
|
||||
RigidBodyControl control = getControl(RigidBodyControl.class);
|
||||
PhysicsSpace space = null;
|
||||
if (control != null) {
|
||||
space = control.getPhysicsSpace();
|
||||
space.remove(this);
|
||||
this.removeControl(control);
|
||||
}
|
||||
int dx = (int) cam.x;
|
||||
int dz = (int) cam.z;
|
||||
if (this.currentCell != null) {
|
||||
dx -= (int) (this.currentCell.x);
|
||||
dz -= (int) (this.currentCell.z);
|
||||
}
|
||||
if (this.currentCell == null || FastMath.abs(dx) > 1 || FastMath.abs(dz) > 1 || (dx != 0 && dz != 0)) {
|
||||
if (this.currentCell != null) {
|
||||
// in case of teleport, otherwise the FastMath.abs(delta) should
|
||||
// never be greater than 1
|
||||
this.removeQuad(1);
|
||||
this.removeQuad(2);
|
||||
this.removeQuad(3);
|
||||
this.removeQuad(4);
|
||||
}
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[0]).mult(this.quadSize - 1), 1));
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[1]).mult(this.quadSize - 1), 2));
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[2]).mult(this.quadSize - 1), 3));
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[3]).mult(this.quadSize - 1), 4));
|
||||
} else if (dx == 0) {
|
||||
if (dz < 0) {
|
||||
// move north
|
||||
this.moveQuad(1, 2);
|
||||
this.moveQuad(3, 4);
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[0]).mult(this.quadSize - 1), 1));
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[2]).mult(this.quadSize - 1), 3));
|
||||
} else {
|
||||
// move south
|
||||
this.moveQuad(2, 1);
|
||||
this.moveQuad(4, 3);
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[1]).mult(this.quadSize - 1), 2));
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[3]).mult(this.quadSize - 1), 4));
|
||||
}
|
||||
} else if (dz == 0) {
|
||||
if (dx < 0) {
|
||||
// move west
|
||||
this.moveQuad(1, 3);
|
||||
this.moveQuad(2, 4);
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[0]).mult(this.quadSize - 1), 1));
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[1]).mult(this.quadSize - 1), 2));
|
||||
} else {
|
||||
// move east
|
||||
this.moveQuad(3, 1);
|
||||
this.moveQuad(4, 2);
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[2]).mult(this.quadSize - 1), 3));
|
||||
this.attachChild(this.createQuadAt(cam.add(this.quadIndex[3]).mult(this.quadSize - 1), 4));
|
||||
}
|
||||
} else {
|
||||
// rare situation to enter into a diagonally placed cell
|
||||
// could not get into this part while testing, as it is handled by moving first
|
||||
// in either horizontally or vertically than the other way
|
||||
// I handle it in the first IF
|
||||
}
|
||||
this.currentCell = cam;
|
||||
this.setLocalTranslation(cam.mult(2 * this.quadSize));
|
||||
|
||||
if (control != null) {
|
||||
control = new RigidBodyControl(new HeightfieldCollisionShape(getHeightMap(), getLocalScale()), 0);
|
||||
this.addControl(control);
|
||||
space.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(String id, TerrainGridListener listener) {
|
||||
this.listeners.put(id, listener);
|
||||
}
|
||||
|
||||
public Vector3f getCurrentCell() {
|
||||
return this.currentCell;
|
||||
}
|
||||
|
||||
public void removeListener(String id) {
|
||||
this.listeners.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaterial(Material mat) {
|
||||
this.material = mat;
|
||||
super.setMaterial(mat);
|
||||
}
|
||||
|
||||
public void setQuadSize(int quadSize) {
|
||||
this.quadSize = quadSize;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.jme3.terrain.geomipmap;
|
||||
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Anthyon
|
||||
*/
|
||||
public interface TerrainGridListener {
|
||||
|
||||
public void gridMoved(Vector3f newCenter);
|
||||
|
||||
}
|
@ -39,12 +39,12 @@ import com.jme3.scene.VertexBuffer.Type;
|
||||
/**
|
||||
* Stores a terrain patch's details so the LOD background thread can update
|
||||
* the actual terrain patch back on the ogl thread.
|
||||
*
|
||||
*
|
||||
* @author Brent Owens
|
||||
*
|
||||
*/
|
||||
public class UpdatedTerrainPatch {
|
||||
|
||||
|
||||
private TerrainPatch updatedPatch;
|
||||
private int newLod;
|
||||
private int previousLod;
|
||||
@ -52,12 +52,12 @@ public class UpdatedTerrainPatch {
|
||||
private IntBuffer newIndexBuffer;
|
||||
private boolean reIndexNeeded = false;
|
||||
private boolean fixEdges = false;
|
||||
|
||||
|
||||
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {
|
||||
this.updatedPatch = updatedPatch;
|
||||
this.newLod = newLod;
|
||||
}
|
||||
|
||||
|
||||
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {
|
||||
this.updatedPatch = updatedPatch;
|
||||
this.newLod = newLod;
|
||||
@ -70,14 +70,14 @@ public class UpdatedTerrainPatch {
|
||||
public String getName() {
|
||||
return updatedPatch.getName();
|
||||
}
|
||||
|
||||
|
||||
protected boolean lodChanged() {
|
||||
if (reIndexNeeded && previousLod != newLod)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected TerrainPatch getUpdatedPatch() {
|
||||
return updatedPatch;
|
||||
}
|
||||
@ -174,7 +174,7 @@ public class UpdatedTerrainPatch {
|
||||
updatedPatch.setLodTop(topLod);
|
||||
updatedPatch.setLodLeft(leftLod);
|
||||
updatedPatch.setLodBottom(bottomLod);
|
||||
if (reIndexNeeded || fixEdges) {
|
||||
if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {
|
||||
updatedPatch.setPreviousLod(previousLod);
|
||||
updatedPatch.getMesh().clearBuffer(Type.Index);
|
||||
updatedPatch.getMesh().setBuffer(Type.Index, 3, newIndexBuffer);
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.jme3.terrain.heightmap;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Anthyon
|
||||
*/
|
||||
public class Grayscale16BitHeightMap extends AbstractHeightMap {
|
||||
|
||||
private BufferedImage image;
|
||||
|
||||
public Grayscale16BitHeightMap() {
|
||||
}
|
||||
|
||||
public Grayscale16BitHeightMap(BufferedImage image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Grayscale16BitHeightMap(String filename) {
|
||||
this(new File(filename));
|
||||
}
|
||||
|
||||
public Grayscale16BitHeightMap(File file) {
|
||||
try {
|
||||
this.image = ImageIO.read(file);
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Grayscale16BitHeightMap.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean load() {
|
||||
return load(false, false);
|
||||
}
|
||||
|
||||
public boolean load(boolean flipX, boolean flipY) {
|
||||
int imageWidth = image.getWidth();
|
||||
int imageHeight = image.getHeight();
|
||||
|
||||
if (imageWidth != imageHeight) {
|
||||
throw new RuntimeException("imageWidth: " + imageWidth
|
||||
+ " != imageHeight: " + imageHeight);
|
||||
}
|
||||
|
||||
Object out = new short[imageWidth * imageHeight];
|
||||
out = image.getData().getDataElements(0, 0, imageWidth, imageHeight, out);
|
||||
short[] values = (short[]) out;
|
||||
heightData = new float[imageWidth * imageHeight];
|
||||
int i = 0;
|
||||
for (int y = 0; y < imageHeight; y++) {
|
||||
for (int x = 0; x < imageWidth; x++, i++) {
|
||||
heightData[i] = heightScale * (values[i] & 0x0000FFFF) / 65536f;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.jme3.terrain.heightmap;
|
||||
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Anthyon
|
||||
*/
|
||||
public interface HeightMapGrid {
|
||||
|
||||
public HeightMap getHeightMapAt(Vector3f location);
|
||||
|
||||
public void setSize(int size);
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.jme3.terrain.heightmap;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.asset.AssetNotFoundException;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.texture.Texture;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.imageio.ImageIO;
|
||||
import jme3tools.converters.ImageToAwt;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Anthyon
|
||||
*/
|
||||
public class ImageBasedHeightMapGrid implements HeightMapGrid {
|
||||
|
||||
private final String textureBase;
|
||||
private final String textureExt;
|
||||
private final AssetManager assetManager;
|
||||
private int size;
|
||||
|
||||
public ImageBasedHeightMapGrid(String textureBase, String textureExt, AssetManager assetManager) {
|
||||
this.textureBase = textureBase;
|
||||
this.textureExt = textureExt;
|
||||
this.assetManager = assetManager;
|
||||
}
|
||||
|
||||
public HeightMap getHeightMapAt(Vector3f location) {
|
||||
// HEIGHTMAP image (for the terrain heightmap)
|
||||
int x = (int) (FastMath.floor(location.x / this.size) * this.size);
|
||||
int z = (int) (FastMath.floor(location.z / this.size) * this.size);
|
||||
AbstractHeightMap heightmap = null;
|
||||
try {
|
||||
final InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(textureBase + "_" + x + "_" + z + "." + textureExt);
|
||||
BufferedImage im = null;
|
||||
if (stream != null) {
|
||||
im = ImageIO.read(stream);
|
||||
} else {
|
||||
im = new BufferedImage(size, size, BufferedImage.TYPE_USHORT_GRAY);
|
||||
}
|
||||
// CREATE HEIGHTMAP
|
||||
heightmap = new Grayscale16BitHeightMap(im);
|
||||
heightmap.setHeightScale(256);
|
||||
heightmap.load();
|
||||
} catch (IOException e) {
|
||||
} catch (AssetNotFoundException e) {
|
||||
}
|
||||
return heightmap;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size - 1;
|
||||
}
|
||||
}
|
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_-512.png
Normal file
After Width: | Height: | Size: 444 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_0.png
Normal file
After Width: | Height: | Size: 437 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_1024.png
Normal file
After Width: | Height: | Size: 424 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_1536.png
Normal file
After Width: | Height: | Size: 358 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_2048.png
Normal file
After Width: | Height: | Size: 370 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_2560.png
Normal file
After Width: | Height: | Size: 415 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_3072.png
Normal file
After Width: | Height: | Size: 438 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_0_512.png
Normal file
After Width: | Height: | Size: 430 KiB |
After Width: | Height: | Size: 420 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_1024_0.png
Normal file
After Width: | Height: | Size: 445 KiB |
After Width: | Height: | Size: 395 KiB |
After Width: | Height: | Size: 410 KiB |
After Width: | Height: | Size: 414 KiB |
After Width: | Height: | Size: 420 KiB |
After Width: | Height: | Size: 442 KiB |
After Width: | Height: | Size: 336 KiB |
After Width: | Height: | Size: 444 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_1536_0.png
Normal file
After Width: | Height: | Size: 428 KiB |
After Width: | Height: | Size: 374 KiB |
After Width: | Height: | Size: 428 KiB |
After Width: | Height: | Size: 420 KiB |
After Width: | Height: | Size: 436 KiB |
After Width: | Height: | Size: 450 KiB |
After Width: | Height: | Size: 446 KiB |
After Width: | Height: | Size: 436 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_2048_0.png
Normal file
After Width: | Height: | Size: 429 KiB |
After Width: | Height: | Size: 416 KiB |
After Width: | Height: | Size: 430 KiB |
After Width: | Height: | Size: 432 KiB |
After Width: | Height: | Size: 414 KiB |
After Width: | Height: | Size: 424 KiB |
After Width: | Height: | Size: 439 KiB |
After Width: | Height: | Size: 426 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_512_0.png
Normal file
After Width: | Height: | Size: 432 KiB |
After Width: | Height: | Size: 338 KiB |
After Width: | Height: | Size: 414 KiB |
After Width: | Height: | Size: 422 KiB |
After Width: | Height: | Size: 425 KiB |
After Width: | Height: | Size: 424 KiB |
BIN
engine/src/test-data/Textures/Terrain/grid/mountains_512_512.png
Normal file
After Width: | Height: | Size: 381 KiB |
184
engine/src/test/jme3test/terrain/TerrainGridTest.java
Normal file
@ -0,0 +1,184 @@
|
||||
package jme3test.terrain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.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.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.terrain.geomipmap.TerrainGrid;
|
||||
import com.jme3.terrain.geomipmap.TerrainLodControl;
|
||||
import com.jme3.terrain.geomipmap.TerrainQuad;
|
||||
import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.WrapMode;
|
||||
|
||||
public class TerrainGridTest extends SimpleApplication {
|
||||
|
||||
private Material mat_terrain;
|
||||
private TerrainQuad terrain;
|
||||
private float grassScale = 64;
|
||||
private float dirtScale = 16;
|
||||
private float rockScale = 128;
|
||||
|
||||
public static void main(final String[] args) {
|
||||
TerrainGridTest app = new TerrainGridTest();
|
||||
app.start();
|
||||
}
|
||||
private CharacterControl player3;
|
||||
|
||||
@Override
|
||||
public void simpleInitApp() {
|
||||
this.flyCam.setMoveSpeed(100f);
|
||||
ScreenshotAppState state = new ScreenshotAppState();
|
||||
this.stateManager.attach(state);
|
||||
|
||||
// TERRAIN TEXTURE material
|
||||
mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
|
||||
mat_terrain.setBoolean("useTriPlanarMapping", false);
|
||||
|
||||
// ALPHA map (for splat textures)
|
||||
mat_terrain.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
|
||||
|
||||
// GRASS texture
|
||||
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
|
||||
grass.setWrap(WrapMode.Repeat);
|
||||
mat_terrain.setTexture("Tex1", grass);
|
||||
mat_terrain.setFloat("Tex1Scale", grassScale);
|
||||
|
||||
// DIRT texture
|
||||
Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
|
||||
dirt.setWrap(WrapMode.Repeat);
|
||||
mat_terrain.setTexture("Tex2", dirt);
|
||||
mat_terrain.setFloat("Tex2Scale", dirtScale);
|
||||
|
||||
// ROCK texture
|
||||
Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
|
||||
rock.setWrap(WrapMode.Repeat);
|
||||
mat_terrain.setTexture("Tex3", rock);
|
||||
mat_terrain.setFloat("Tex3Scale", rockScale);
|
||||
|
||||
this.terrain = new TerrainGrid("terrain", 65, 1025, new ImageBasedHeightMapGrid("Textures/Terrain/grid/mountains", "png",
|
||||
this.assetManager));
|
||||
|
||||
this.terrain.setMaterial(this.mat_terrain);
|
||||
this.terrain.setLocalTranslation(0, 0, 0);
|
||||
this.terrain.setLocalScale(2f, 1f, 2f);
|
||||
this.rootNode.attachChild(this.terrain);
|
||||
|
||||
List<Camera> cameras = new ArrayList<Camera>();
|
||||
cameras.add(this.getCamera());
|
||||
TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
|
||||
this.terrain.addControl(control);
|
||||
|
||||
BulletAppState bulletAppState = new BulletAppState();
|
||||
stateManager.attach(bulletAppState);
|
||||
|
||||
RigidBodyControl body = new RigidBodyControl(new HeightfieldCollisionShape(terrain.getHeightMap(), terrain.getLocalScale()), 0);
|
||||
terrain.addControl(body);
|
||||
bulletAppState.getPhysicsSpace().add(terrain);
|
||||
|
||||
this.getCamera().setLocation(new Vector3f(0, 256, 0));
|
||||
|
||||
this.viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
|
||||
|
||||
CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(0.5f, 1.8f, 1);
|
||||
this.player3 = new CharacterControl(capsuleShape, 0.5f);
|
||||
this.player3.setJumpSpeed(20);
|
||||
this.player3.setFallSpeed(30);
|
||||
this.player3.setGravity(30);
|
||||
|
||||
this.player3.setPhysicsLocation(new Vector3f(0, 256, 0));
|
||||
|
||||
bulletAppState.getPhysicsSpace().add(this.player3);
|
||||
|
||||
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.addMapping("Gravity", new KeyTrigger(KeyInput.KEY_G));
|
||||
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");
|
||||
this.inputManager.addListener(this.actionListener, "Gravity");
|
||||
}
|
||||
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) {
|
||||
TerrainGridTest.this.left = true;
|
||||
} else {
|
||||
TerrainGridTest.this.left = false;
|
||||
}
|
||||
} else if (name.equals("Rights")) {
|
||||
if (keyPressed) {
|
||||
TerrainGridTest.this.right = true;
|
||||
} else {
|
||||
TerrainGridTest.this.right = false;
|
||||
}
|
||||
} else if (name.equals("Ups")) {
|
||||
if (keyPressed) {
|
||||
TerrainGridTest.this.up = true;
|
||||
} else {
|
||||
TerrainGridTest.this.up = false;
|
||||
}
|
||||
} else if (name.equals("Downs")) {
|
||||
if (keyPressed) {
|
||||
TerrainGridTest.this.down = true;
|
||||
} else {
|
||||
TerrainGridTest.this.down = false;
|
||||
}
|
||||
} else if (name.equals("Jumps")) {
|
||||
TerrainGridTest.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());
|
||||
}
|
||||
|
||||
this.player3.setWalkDirection(this.walkDirection);
|
||||
this.cam.setLocation(this.player3.getPhysicsLocation());
|
||||
}
|
||||
}
|