Added my own noise library as jar (noise-0.0.1-SNAPSHOT.jar), hope I didn't break any build stuff

TerrainGridTest is running smooth now using 257x257 sized TerrainQuads with fractal based heightmaps calculated on the fly.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7529 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
ant..om 14 years ago
parent 1fbd05a3ca
commit 58ede837c6
  1. 2
      engine/lib/nblibraries.properties
  2. BIN
      engine/lib/noise/noise-0.0.1-SNAPSHOT.jar
  3. 3
      engine/nbproject/project.properties
  4. 62
      engine/src/terrain/com/jme3/terrain/MapUtils.java
  5. 124
      engine/src/terrain/com/jme3/terrain/geomipmap/LRUCache.java
  6. 168
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
  7. 44
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
  8. 2
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
  9. 75
      engine/src/terrain/com/jme3/terrain/heightmap/FractalHeightMapGrid.java
  10. 8
      engine/src/terrain/com/jme3/terrain/heightmap/ImageBasedHeightMapGrid.java
  11. 55
      engine/src/test/jme3test/terrain/TerrainGridTest.java

@ -54,4 +54,6 @@ libs.swing-layout.javadoc=\
${base}/swing-layout/swing-layout-1.0.4-doc.zip
libs.swing-layout.src=\
${base}/swing-layout/swing-layout-1.0.4-src.zip
libs.noise.classpath=\
${base}/noise/noise-0.0.1-SNAPSHOT.jar

@ -39,7 +39,8 @@ javac.classpath=\
${libs.lwjgl.classpath}:\
${libs.jheora.classpath}:\
${libs.niftygui1.3.classpath}:\
${libs.jme3-test-data.classpath}
${libs.jme3-test-data.classpath}:\
${libs.noise.classpath}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false

@ -0,0 +1,62 @@
package com.jme3.terrain;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import org.novyon.noise.ShaderUtils;
public class MapUtils {
public static FloatBuffer clip(FloatBuffer src, int origSize, int newSize, int offset) {
FloatBuffer result = FloatBuffer.allocate(newSize * newSize);
float[] orig = src.array();
for (int i = offset; i < offset + newSize; i++) {
result.put(orig, i * origSize + offset, newSize);
}
return result;
}
public static BufferedImage toGrayscale16Image(FloatBuffer buff, int size) {
BufferedImage retval = new BufferedImage(size, size, BufferedImage.TYPE_USHORT_GRAY);
buff.rewind();
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
short c = (short) (ShaderUtils.clamp(buff.get(), 0, 1) * 65532);
retval.getRaster().setDataElements(x, y, new short[] { c });
}
}
return retval;
}
public static BufferedImage toGrayscaleRGBImage(FloatBuffer buff, int size) {
BufferedImage retval = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
buff.rewind();
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
int c = (int) (ShaderUtils.clamp(buff.get(), 0, 1) * 255);
retval.setRGB(x, y, 0xFF000000 | c << 16 | c << 8 | c);
}
}
return retval;
}
public static void saveImage(BufferedImage im, String file) {
MapUtils.saveImage(im, new File(file));
}
public static void saveImage(BufferedImage im, File file) {
try {
ImageIO.write(im, "PNG", file);
Logger.getLogger(MapUtils.class.getCanonicalName()).info("Saved image as : " + file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}

@ -0,0 +1,124 @@
package com.jme3.terrain.geomipmap;
// Copyright 2007 Christian d'Heureuse, Inventec Informatik AG, Zurich,
// Switzerland
// www.source-code.biz, www.inventec.ch/chdh
//
// This module is multi-licensed and may be used under the terms
// of any of the following licenses:
//
// EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
// LGPL, GNU Lesser General Public License, V2 or later,
// http://www.gnu.org/licenses/lgpl.html
// GPL, GNU General Public License, V2 or later,
// http://www.gnu.org/licenses/gpl.html
// AL, Apache License, V2.0 or later, http://www.apache.org/licenses
// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
//
// Please contact the author if you need another license.
// This module is provided "as is", without warranties of any kind.
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* An LRU cache, based on <code>LinkedHashMap</code>.
*
* <p>
* This cache has a fixed maximum number of elements (<code>cacheSize</code>).
* If the cache is full and another entry is added, the LRU (least recently
* used) entry is dropped.
*
* <p>
* This class is thread-safe. All methods of this class are synchronized.
*
* <p>
* Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
* Multi-licensed: EPL / LGPL / GPL / AL / BSD.
*/
public class LRUCache<K, V> {
private static final float hashTableLoadFactor = 0.75f;
private LinkedHashMap<K, V> map;
private int cacheSize;
/**
* Creates a new LRU cache.
*
* @param cacheSize
* the maximum number of entries that will be kept in this cache.
*/
public LRUCache(int cacheSize) {
this.cacheSize = cacheSize;
int hashTableCapacity = (int) Math.ceil(cacheSize / LRUCache.hashTableLoadFactor) + 1;
this.map = new LinkedHashMap<K, V>(hashTableCapacity, LRUCache.hashTableLoadFactor, true) {
// (an anonymous inner class)
private static final long serialVersionUID = 1;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return this.size() > LRUCache.this.cacheSize;
}
};
}
/**
* Retrieves an entry from the cache.<br>
* The retrieved entry becomes the MRU (most recently used) entry.
*
* @param key
* the key whose associated value is to be returned.
* @return the value associated to this key, or null if no value with this
* key exists in the cache.
*/
public synchronized V get(K key) {
return this.map.get(key);
}
/**
* Adds an entry to this cache.
* The new entry becomes the MRU (most recently used) entry.
* If an entry with the specified key already exists in the cache, it is
* replaced by the new entry.
* If the cache is full, the LRU (least recently used) entry is removed from
* the cache.
*
* @param key
* the key with which the specified value is to be associated.
* @param value
* a value to be associated with the specified key.
*/
public synchronized void put(K key, V value) {
this.map.put(key, value);
}
/**
* Clears the cache.
*/
public synchronized void clear() {
this.map.clear();
}
/**
* Returns the number of used entries in the cache.
*
* @return the number of entries currently in the cache.
*/
public synchronized int usedEntries() {
return this.map.size();
}
/**
* Returns a <code>Collection</code> that contains a copy of all cache
* entries.
*
* @return a <code>Collection</code> with a copy of the cache content.
*/
public synchronized Collection<Map.Entry<K, V>> getAll() {
return new ArrayList<Map.Entry<K, V>>(this.map.entrySet());
}
} // end class LRUCache

@ -11,6 +11,8 @@ import com.jme3.terrain.heightmap.HeightMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.material.Material;
@ -35,6 +37,37 @@ public class TerrainGrid extends TerrainQuad {
private Vector3f[] quadIndex;
private Map<String, TerrainGridListener> listeners = new HashMap<String, TerrainGridListener>();
private Material material;
private LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(16);
private class UpdateQuadCache implements Runnable {
private final Vector3f location;
private final boolean centerOnly;
public UpdateQuadCache(Vector3f location) {
this.location = location;
this.centerOnly = false;
}
public UpdateQuadCache(Vector3f location, boolean centerOnly) {
this.location = location;
this.centerOnly = centerOnly;
}
public void run() {
for (int i = centerOnly ? 1 : 0; i < (centerOnly ? 3 : 4); i++) {
for (int j = centerOnly ? 1 : 0; j < (centerOnly ? 3 : 4); j++) {
Vector3f temp = location.add(quadIndex[i * 4 + j]);
if (cache.get(temp) == null) {
HeightMap heightMapAt = heightMapGrid.getHeightMapAt(temp);
TerrainQuad q = new TerrainQuad(getName() + "Quad" + temp, patchSize, quadSize, heightMapAt == null ? null : heightMapAt.getHeightMap(), lodCalculatorFactory);
cache.put(temp, q);
}
}
}
}
}
public TerrainGrid(String name, int patchSize, int size, Vector3f stepScale, HeightMapGrid heightMapGrid, int totalSize,
Vector2f offset, float offsetAmount, LodCalculatorFactory lodCalculatorFactory) {
@ -57,7 +90,11 @@ public class TerrainGrid extends TerrainQuad {
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)};
this.quadIndex = new Vector3f[]{
new Vector3f(-1, 0, -1), new Vector3f(-1, 0, 0), new Vector3f(-1, 0, 1), new Vector3f(-1, 0, 2),
new Vector3f(0, 0, -1), new Vector3f(0, 0, 0), new Vector3f(0, 0, 1), new Vector3f(0, 0, 2),
new Vector3f(1, 0, -1), new Vector3f(1, 0, 0), new Vector3f(1, 0, 1), new Vector3f(1, 0, 2),
new Vector3f(2, 0, -1), new Vector3f(2, 0, 0), new Vector3f(2, 0, 1), new Vector3f(2, 0, 2)};
updateChildrens(Vector3f.ZERO);
}
@ -93,7 +130,7 @@ public class TerrainGrid extends TerrainQuad {
}
}
//super.update(locations);
super.update(locations);
}
public Vector3f getCell(Vector3f location) {
@ -102,7 +139,9 @@ public class TerrainGrid extends TerrainQuad {
}
protected void removeQuad(int idx) {
this.detachChild(this.getQuad(idx));
if (this.getQuad(idx) != null) {
this.detachChild(this.getQuad(idx));
}
}
protected void moveQuad(int from, int to) {
@ -112,16 +151,66 @@ public class TerrainGrid extends TerrainQuad {
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]);
protected void attachQuadAt(TerrainQuad q, int quadrant) {
q.setMaterial(this.material);
q.setLocalTranslation(quadOrigins[quadrant - 1]);
q.setQuadrant((short) quadrant);
return q;
this.attachChild(q);
}
private void updateChildrens(Vector3f cam) {
TerrainQuad q1 = cache.get(cam.add(quadIndex[5]));
TerrainQuad q2 = cache.get(cam.add(quadIndex[6]));
TerrainQuad q3 = cache.get(cam.add(quadIndex[9]));
TerrainQuad q4 = cache.get(cam.add(quadIndex[10]));
int dx = 0;
int dy = 0;
if (currentCell != null) {
dx = (int) (cam.x - currentCell.x);
dy = (int) (cam.z - currentCell.z);
}
int kxm = 0;
int kxM = 4;
int kym = 0;
int kyM = 4;
if (dx == -1) {
kxM = 3;
} else if (dx == 1) {
kxm = 1;
}
if (dy == -1) {
kyM = 3;
} else if (dy == 1) {
kym = 1;
}
for (int i=kym; i<kyM; i++) {
for (int j = kxm; j < kxM; j++) {
cache.get(cam.add(quadIndex[i * 4 + j]));
}
}
if (q1 == null || q2 == null || q3 == null || q4 == null) {
try {
executor.submit(new UpdateQuadCache(cam, true)).get();
q1 = cache.get(cam.add(quadIndex[5]));
q2 = cache.get(cam.add(quadIndex[6]));
q3 = cache.get(cam.add(quadIndex[9]));
q4 = cache.get(cam.add(quadIndex[10]));
} catch (InterruptedException ex) {
Logger.getLogger(TerrainGrid.class.getName()).log(Level.SEVERE, null, ex);
return;
} catch (ExecutionException ex) {
Logger.getLogger(TerrainGrid.class.getName()).log(Level.SEVERE, null, ex);
return;
}
}
executor.execute(new UpdateQuadCache(cam));
RigidBodyControl control = getControl(RigidBodyControl.class);
PhysicsSpace space = null;
if (control != null) {
@ -129,59 +218,16 @@ public class TerrainGrid extends TerrainQuad {
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.removeQuad(1);
this.removeQuad(2);
this.removeQuad(3);
this.removeQuad(4);
attachQuadAt(q1, 1);
attachQuadAt(q2, 2);
attachQuadAt(q3, 3);
attachQuadAt(q4, 4);
this.currentCell = cam;
this.setLocalTranslation(cam.mult(2 * this.quadSize));

@ -29,7 +29,6 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.terrain.geomipmap;
import com.jme3.export.InputCapsule;
@ -69,7 +68,7 @@ public class TerrainLodControl extends AbstractControl {
public TerrainLodControl() {
}
/**
/**
* Only uses the first camera right now.
* @param terrain to act upon (must be a Spatial)
* @param cameras one or more cameras to reference for LOD calc
@ -81,49 +80,52 @@ public class TerrainLodControl extends AbstractControl {
this.cameras = cameras;
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
@Override
protected void controlUpdate(float tpf) {
//list of cameras for when terrain supports multiple cameras (ie split screen)
@Override
protected void controlUpdate(float tpf) {
//list of cameras for when terrain supports multiple cameras (ie split screen)
if (cameras != null) {
if (cameraLocations.isEmpty() && !cameras.isEmpty()) {
for (Camera c : cameras) // populate them
{
cameraLocations.add(c.getLocation());
}
}
terrain.update(cameraLocations);
}
}
}
public Control cloneForSpatial(Spatial spatial) {
if (spatial instanceof Terrain) {
public Control cloneForSpatial(Spatial spatial) {
if (spatial instanceof Terrain) {
List<Camera> cameraClone = new ArrayList<Camera>();
if (cameras != null) {
for (Camera c : cameras)
cameraClone.add(c);
for (Camera c : cameras) {
cameraClone.add(c);
}
}
return new TerrainLodControl((TerrainQuad)spatial, cameraClone);
return new TerrainLodControl((TerrainQuad) spatial, cameraClone);
}
return null;
}
return null;
}
public void setCameras(List<Camera> cameras) {
this.cameras = cameras;
cameraLocations.clear();
for (Camera c : cameras)
for (Camera c : cameras) {
cameraLocations.add(c.getLocation());
}
}
@Override
public void setSpatial(Spatial spatial) {
super.setSpatial(spatial);
if (spatial instanceof TerrainQuad)
this.terrain = (TerrainQuad)spatial;
if (spatial instanceof TerrainQuad) {
this.terrain = (TerrainQuad) spatial;
}
}
public void setTerrain(TerrainQuad terrain) {

@ -108,7 +108,7 @@ public class TerrainQuad extends Node implements Terrain {
private TerrainPicker picker;
private ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
protected ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread th = new Thread(r);
th.setDaemon(true);

@ -0,0 +1,75 @@
package com.jme3.terrain.heightmap;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.FloatBuffer;
import javax.imageio.ImageIO;
import org.novyon.noise.Basis;
import com.jme3.math.Vector3f;
import com.jme3.terrain.MapUtils;
public class FractalHeightMapGrid implements HeightMapGrid {
public class FloatBufferHeightMap extends AbstractHeightMap {
private final FloatBuffer buffer;
public FloatBufferHeightMap(FloatBuffer buffer) {
this.buffer = buffer;
}
@Override
public boolean load() {
this.heightData = this.buffer.array();
return true;
}
}
private int size;
private final Basis base;
private final String cacheDir;
private final float heightScale;
public FractalHeightMapGrid(Basis base, String cacheDir, float heightScale) {
this.base = base;
this.cacheDir = cacheDir;
this.heightScale = heightScale;
}
@Override
public HeightMap getHeightMapAt(Vector3f location) {
AbstractHeightMap heightmap = null;
if (this.cacheDir != null && new File(this.cacheDir, "terrain_" + (int) location.x + "_" + (int) location.z + ".png").exists()) {
try {
BufferedImage im = null;
im = ImageIO.read(new File(this.cacheDir, "terrain_" + (int) location.x + "_" + (int) location.z + ".png"));
heightmap = new Grayscale16BitHeightMap(im);
heightmap.setHeightScale(256);
} catch (IOException e) {}
} else {
FloatBuffer buffer = this.base.getBuffer(location.x * (this.size - 1), location.z * (this.size - 1), 0, this.size);
if (this.cacheDir != null) {
MapUtils.saveImage(MapUtils.toGrayscale16Image(buffer, this.size), new File(this.cacheDir, "terrain_" + (int) location.x
+ "_" + (int) location.z + ".png"));
}
float[] arr = buffer.array();
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * this.heightScale;
}
heightmap = new FloatBufferHeightMap(buffer);
}
heightmap.load();
return heightmap;
}
@Override
public void setSize(int size) {
this.size = size;
}
}

@ -12,6 +12,8 @@ import com.jme3.texture.Texture;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import jme3tools.converters.ImageToAwt;
@ -34,16 +36,18 @@ public class ImageBasedHeightMapGrid implements HeightMapGrid {
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);
int x = (int) location.x;
int z = (int) location.z;
AbstractHeightMap heightmap = null;
try {
Logger.getLogger(ImageBasedHeightMapGrid.class.getCanonicalName()).log(Level.INFO, "Loading heightmap from file: " + textureBase + "_" + x + "_" + z + "." + textureExt);
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);
Logger.getLogger(ImageBasedHeightMapGrid.class.getCanonicalName()).log(Level.INFO, "File: " + textureBase + "_" + x + "_" + z + "." + textureExt + " not found, loading zero heightmap instead");
}
// CREATE HEIGHTMAP
heightmap = new Grayscale16BitHeightMap(im);

@ -20,9 +20,18 @@ 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.FractalHeightMapGrid;
import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import org.novyon.noise.ShaderUtils;
import org.novyon.noise.basis.FilteredBasis;
import org.novyon.noise.filter.IterativeFilter;
import org.novyon.noise.filter.OptimizedErode;
import org.novyon.noise.filter.PerturbFilter;
import org.novyon.noise.filter.SmoothFilter;
import org.novyon.noise.fractal.FractalSum;
import org.novyon.noise.modulator.NoiseModulator;
public class TerrainGridTest extends SimpleApplication {
@ -31,13 +40,18 @@ public class TerrainGridTest extends SimpleApplication {
private float grassScale = 64;
private float dirtScale = 16;
private float rockScale = 128;
private boolean usePhysics = false;
private boolean usePhysics = true;
public static void main(final String[] args) {
TerrainGridTest app = new TerrainGridTest();
app.start();
}
private CharacterControl player3;
private FractalSum base;
private PerturbFilter perturb;
private OptimizedErode therm;
private SmoothFilter smooth;
private IterativeFilter iterate;
@Override
public void simpleInitApp() {
@ -70,8 +84,43 @@ public class TerrainGridTest extends SimpleApplication {
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.base = new FractalSum();
this.base.setRoughness(0.7f);
this.base.setFrequency(1.0f);
this.base.setAmplitude(1.0f);
this.base.setLacunarity(2.12f);
this.base.setOctaves(8);
this.base.setScale(0.02125f);
this.base.addModulator(new NoiseModulator() {
@Override
public float value(float... in) {
return ShaderUtils.clamp(in[0] * 0.5f + 0.5f, 0, 1);
}
});
FilteredBasis ground = new FilteredBasis(this.base);
this.perturb = new PerturbFilter();
this.perturb.setMagnitude(0.119f);
this.therm = new OptimizedErode();
this.therm.setRadius(5);
this.therm.setTalus(0.011f);
this.smooth = new SmoothFilter();
this.smooth.setRadius(1);
this.smooth.setEffect(0.7f);
this.iterate = new IterativeFilter();
this.iterate.addPreFilter(this.perturb);
this.iterate.addPostFilter(this.smooth);
this.iterate.setFilter(this.therm);
this.iterate.setIterations(1);
ground.addPreFilter(this.iterate);
this.terrain = new TerrainGrid("terrain", 65, 257, new FractalHeightMapGrid(ground, null, 256f));
this.terrain.setMaterial(this.mat_terrain);
this.terrain.setLocalTranslation(0, 0, 0);

Loading…
Cancel
Save