diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java new file mode 100644 index 000000000..70827b292 --- /dev/null +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java @@ -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 listeners = new HashMap(); + 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 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; + } +} diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java new file mode 100644 index 000000000..d4aab45d9 --- /dev/null +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGridListener.java @@ -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); + +} diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java index 0ce981d2d..1d2cc3bee 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java @@ -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); diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/Grayscale16BitHeightMap.java b/engine/src/terrain/com/jme3/terrain/heightmap/Grayscale16BitHeightMap.java new file mode 100644 index 000000000..ed93af890 --- /dev/null +++ b/engine/src/terrain/com/jme3/terrain/heightmap/Grayscale16BitHeightMap.java @@ -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; + } +} diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java b/engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java new file mode 100644 index 000000000..f06608aa5 --- /dev/null +++ b/engine/src/terrain/com/jme3/terrain/heightmap/HeightMapGrid.java @@ -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); + +} diff --git a/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java b/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java new file mode 100644 index 000000000..a467f9783 --- /dev/null +++ b/engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java @@ -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; + } +} diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_-512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_-512.png new file mode 100644 index 000000000..341d16bfc Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_-512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_0.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_0.png new file mode 100644 index 000000000..16a98d016 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_0.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_1024.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_1024.png new file mode 100644 index 000000000..24be0101c Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_1024.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_1536.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_1536.png new file mode 100644 index 000000000..d7d60dd85 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_1536.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_2048.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_2048.png new file mode 100644 index 000000000..8cc3d62ec Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_2048.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_2560.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_2560.png new file mode 100644 index 000000000..df091caa5 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_2560.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_3072.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_3072.png new file mode 100644 index 000000000..1740006d8 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_3072.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_0_512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_0_512.png new file mode 100644 index 000000000..25a255307 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_0_512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_-512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_-512.png new file mode 100644 index 000000000..f52a981fa Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_-512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_0.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_0.png new file mode 100644 index 000000000..51e38b64a Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_0.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_1024.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_1024.png new file mode 100644 index 000000000..19549b670 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_1024.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_1536.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_1536.png new file mode 100644 index 000000000..c4f1fc528 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_1536.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_2048.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_2048.png new file mode 100644 index 000000000..af7acdea8 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_2048.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_2560.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_2560.png new file mode 100644 index 000000000..09e468cb8 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_2560.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_3072.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_3072.png new file mode 100644 index 000000000..703edf942 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_3072.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1024_512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_512.png new file mode 100644 index 000000000..a1a83c017 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1024_512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_-512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_-512.png new file mode 100644 index 000000000..4b853b45e Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_-512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_0.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_0.png new file mode 100644 index 000000000..ac49d1535 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_0.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_1024.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_1024.png new file mode 100644 index 000000000..38e61f429 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_1024.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_1536.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_1536.png new file mode 100644 index 000000000..9a1855b75 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_1536.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_2048.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_2048.png new file mode 100644 index 000000000..e5b374813 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_2048.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_2560.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_2560.png new file mode 100644 index 000000000..367409f73 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_2560.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_3072.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_3072.png new file mode 100644 index 000000000..5c534934a Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_3072.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_1536_512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_512.png new file mode 100644 index 000000000..e1c90844c Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_1536_512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_-512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_-512.png new file mode 100644 index 000000000..99957762b Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_-512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_0.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_0.png new file mode 100644 index 000000000..718dab60c Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_0.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_1024.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_1024.png new file mode 100644 index 000000000..1d7a16a1a Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_1024.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_1536.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_1536.png new file mode 100644 index 000000000..34e3d526f Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_1536.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_2048.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_2048.png new file mode 100644 index 000000000..17c3ed46c Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_2048.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_2560.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_2560.png new file mode 100644 index 000000000..2374ede91 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_2560.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_3072.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_3072.png new file mode 100644 index 000000000..ba372cfd2 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_3072.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_2048_512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_512.png new file mode 100644 index 000000000..a8b6317c7 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_2048_512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_-512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_-512.png new file mode 100644 index 000000000..fe2994eed Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_-512.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_0.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_0.png new file mode 100644 index 000000000..3283e98e6 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_0.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_1024.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_1024.png new file mode 100644 index 000000000..df47024b0 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_1024.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_1536.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_1536.png new file mode 100644 index 000000000..ee32fa535 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_1536.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_2048.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_2048.png new file mode 100644 index 000000000..f2e7b3b60 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_2048.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_2560.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_2560.png new file mode 100644 index 000000000..4c3a0ada4 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_2560.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_3072.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_3072.png new file mode 100644 index 000000000..c70af7f70 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_3072.png differ diff --git a/engine/src/test-data/Textures/Terrain/grid/mountains_512_512.png b/engine/src/test-data/Textures/Terrain/grid/mountains_512_512.png new file mode 100644 index 000000000..a4f32aca2 Binary files /dev/null and b/engine/src/test-data/Textures/Terrain/grid/mountains_512_512.png differ diff --git a/engine/src/test/jme3test/terrain/TerrainGridTest.java b/engine/src/test/jme3test/terrain/TerrainGridTest.java new file mode 100644 index 000000000..a0b46aa29 --- /dev/null +++ b/engine/src/test/jme3test/terrain/TerrainGridTest.java @@ -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 cameras = new ArrayList(); + 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()); + } +}