* refactored TerrainLOD Control and removed some legacy classes for it, made it easier to use

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8233 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 14 years ago
parent b8618eba26
commit 3383e2e086
  1. 3
      engine/src/terrain/com/jme3/terrain/Terrain.java
  2. 28
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
  3. 17
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
  4. 47
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
  5. 120
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
  6. 78
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java
  7. 5
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculator.java
  8. 1
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodCalculatorFactory.java
  9. 5
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodDistanceCalculatorFactory.java
  10. 2
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodPerspectiveCalculatorFactory.java
  11. 8
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/LodThreshold.java
  12. 28
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java
  13. 22
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/SimpleLodThreshold.java
  14. 2
      engine/src/test/jme3test/terrain/TerrainFractalGridTest.java
  15. 2
      engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java
  16. 2
      engine/src/test/jme3test/terrain/TerrainGridTest.java
  17. 5
      engine/src/test/jme3test/terrain/TerrainTest.java
  18. 29
      engine/src/test/jme3test/terrain/TerrainTestAdvanced.java
  19. 2
      engine/src/test/jme3test/terrain/TerrainTestCollision.java
  20. 2
      engine/src/test/jme3test/terrain/TerrainTestModifyHeight.java
  21. 2
      engine/src/test/jme3test/terrain/TerrainTestReadWrite.java

@ -34,6 +34,7 @@ package com.jme3.terrain;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import java.util.List; import java.util.List;
/** /**
@ -133,7 +134,7 @@ public interface Terrain {
* @param location: the Camera's location. A list of one camera location is normal * @param location: the Camera's location. A list of one camera location is normal
* if you just have one camera in your scene. * if you just have one camera in your scene.
*/ */
public void update(List<Vector3f> location); public void update(List<Vector3f> location, LodCalculator lodCalculator);
/** /**
* Lock or unlock the meshes of this terrain. * Lock or unlock the meshes of this terrain.

@ -46,6 +46,7 @@ import com.jme3.material.Material;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory; import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory;
import com.jme3.terrain.geomipmap.lodcalc.LodDistanceCalculatorFactory; import com.jme3.terrain.geomipmap.lodcalc.LodDistanceCalculatorFactory;
import com.jme3.terrain.heightmap.HeightMapGrid; import com.jme3.terrain.heightmap.HeightMapGrid;
@ -88,7 +89,7 @@ public class TerrainGrid extends TerrainQuad {
if (q == null) { if (q == null) {
// create the new Quad since it doesn't exist // create the new Quad since it doesn't exist
HeightMap heightMapAt = heightMapGrid.getHeightMapAt(temp); HeightMap heightMapAt = heightMapGrid.getHeightMapAt(temp);
q = new TerrainQuad(getName() + "Quad" + temp, patchSize, quadSize, totalSize, heightMapAt == null ? null : heightMapAt.getHeightMap(), lodCalculatorFactory); q = new TerrainQuad(getName() + "Quad" + temp, patchSize, quadSize, totalSize, heightMapAt == null ? null : heightMapAt.getHeightMap());
q.setMaterial(material.clone()); q.setMaterial(material.clone());
log.log(Level.FINE, "Loaded TerrainQuad {0}", q.getName()); log.log(Level.FINE, "Loaded TerrainQuad {0}", q.getName());
} }
@ -131,7 +132,7 @@ public class TerrainGrid extends TerrainQuad {
} }
public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid, public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid,
Vector2f offset, float offsetAmount, LodCalculatorFactory lodCalculatorFactory) { Vector2f offset, float offsetAmount) {
this.name = name; this.name = name;
this.patchSize = patchSize; this.patchSize = patchSize;
this.size = maxVisibleSize; this.size = maxVisibleSize;
@ -143,11 +144,11 @@ public class TerrainGrid extends TerrainQuad {
this.totalSize = maxVisibleSize; this.totalSize = maxVisibleSize;
this.offset = offset; this.offset = offset;
this.offsetAmount = offsetAmount; this.offsetAmount = offsetAmount;
this.lodCalculatorFactory = lodCalculatorFactory; //this.lodCalculatorFactory = lodCalculatorFactory;
this.gridOffset = new int[]{0,0}; this.gridOffset = new int[]{0,0};
if (lodCalculatorFactory == null) { //if (lodCalculatorFactory == null) {
lodCalculatorFactory = new LodDistanceCalculatorFactory(); // lodCalculatorFactory = new LodDistanceCalculatorFactory();
} //}
this.quadIndex = new Vector3f[]{ this.quadIndex = new Vector3f[]{
new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2), new Vector3f(-1, 0, 2), new Vector3f(0, 0, 2), new Vector3f(1, 0, 2), new Vector3f(2, 0, 2),
new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1), new Vector3f(-1, 0, 1), new Vector3f(0, 0, 1), new Vector3f(1, 0, 1), new Vector3f(2, 0, 1),
@ -157,17 +158,12 @@ public class TerrainGrid extends TerrainQuad {
addControl(new UpdateControl()); addControl(new UpdateControl());
} }
public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid, public TerrainGrid(String name, int patchSize, int maxVisibleSize, Vector3f scale, HeightMapGrid heightMapGrid) {
LodCalculatorFactory lodCalculatorFactory) { this(name, patchSize, maxVisibleSize, scale, heightMapGrid, new Vector2f(), 0);
this(name, patchSize, maxVisibleSize, scale, heightMapGrid, new Vector2f(), 0, lodCalculatorFactory);
}
public TerrainGrid(String name, int patchSize, int maxVisibleSize, HeightMapGrid heightMapGrid, LodCalculatorFactory lodCalculatorFactory) {
this(name, patchSize, maxVisibleSize, Vector3f.UNIT_XYZ, heightMapGrid, lodCalculatorFactory);
} }
public TerrainGrid(String name, int patchSize, int maxVisibleSize, HeightMapGrid heightMapGrid) { public TerrainGrid(String name, int patchSize, int maxVisibleSize, HeightMapGrid heightMapGrid) {
this(name, patchSize, maxVisibleSize, heightMapGrid, null); this(name, patchSize, maxVisibleSize, Vector3f.UNIT_XYZ, heightMapGrid);
} }
public TerrainGrid() { public TerrainGrid() {
@ -185,7 +181,7 @@ public class TerrainGrid extends TerrainQuad {
} }
@Override @Override
public void update(List<Vector3f> locations) { public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
// for now, only the first camera is handled. // for now, only the first camera is handled.
// to accept more, there are two ways: // 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 // 1: every camera has an associated grid, then the location is not enough to identify which camera location has changed
@ -203,7 +199,7 @@ public class TerrainGrid extends TerrainQuad {
l.gridMoved(camCell); l.gridMoved(camCell);
} }
} }
super.update(locations); super.update(locations, lodCalculator);
} }
public Vector3f getCell(Vector3f location) { public Vector3f getCell(Vector3f location) {

@ -46,6 +46,8 @@ import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl; import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.terrain.Terrain; import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -65,6 +67,7 @@ public class TerrainLodControl extends AbstractControl {
private Terrain terrain; private Terrain terrain;
private List<Camera> cameras; private List<Camera> cameras;
private List<Vector3f> cameraLocations = new ArrayList<Vector3f>(); private List<Vector3f> cameraLocations = new ArrayList<Vector3f>();
private LodCalculator lodCalculator;
public TerrainLodControl() { public TerrainLodControl() {
} }
@ -74,6 +77,7 @@ public class TerrainLodControl extends AbstractControl {
cams.add(camera); cams.add(camera);
this.terrain = terrain; this.terrain = terrain;
this.cameras = cams; this.cameras = cams;
lodCalculator = new DistanceLodCalculator(); // a default calculator
} }
/** /**
@ -101,7 +105,7 @@ public class TerrainLodControl extends AbstractControl {
cameraLocations.add(c.getLocation()); cameraLocations.add(c.getLocation());
} }
} }
terrain.update(cameraLocations); terrain.update(cameraLocations, lodCalculator);
} }
} }
@ -144,11 +148,21 @@ public class TerrainLodControl extends AbstractControl {
this.terrain = terrain; this.terrain = terrain;
} }
public LodCalculator getLodCalculator() {
return lodCalculator;
}
public void setLodCalculator(LodCalculator lodCalculator) {
this.lodCalculator = lodCalculator;
}
@Override @Override
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
super.write(ex); super.write(ex);
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write((Node)terrain, "terrain", null); oc.write((Node)terrain, "terrain", null);
oc.write(lodCalculator, "lodCalculator", null);
} }
@Override @Override
@ -156,5 +170,6 @@ public class TerrainLodControl extends AbstractControl {
super.read(im); super.read(im);
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
terrain = (Terrain) ic.readSavable("terrain", null); terrain = (Terrain) ic.readSavable("terrain", null);
lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
} }
} }

@ -109,8 +109,8 @@ public class TerrainPatch extends Geometry {
// amount the patch has been shifted. // amount the patch has been shifted.
protected float offsetAmount; protected float offsetAmount;
protected LodCalculator lodCalculator; //protected LodCalculator lodCalculator;
protected LodCalculatorFactory lodCalculatorFactory; //protected LodCalculatorFactory lodCalculatorFactory;
protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour; protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;
protected boolean searchedForNeighboursAlready = false; protected boolean searchedForNeighboursAlready = false;
@ -234,20 +234,7 @@ public class TerrainPatch extends Geometry {
return maxLod; return maxLod;
} }
protected void reIndexGeometry(HashMap<String,UpdatedTerrainPatch> updated, boolean useVariableLod) {
/**
* Delegates to the lodCalculator that was passed in.
* @param locations all possible camera locations
* @param updates update objects that may or may not contain this terrain patch
* @return true if the geometry needs re-indexing
*/
protected boolean calculateLod(List<Vector3f> locations, HashMap<String,UpdatedTerrainPatch> updates) {
return lodCalculator.calculateLod(locations, updates);
}
protected void reIndexGeometry(HashMap<String,UpdatedTerrainPatch> updated) {
UpdatedTerrainPatch utp = updated.get(getName()); UpdatedTerrainPatch utp = updated.get(getName());
@ -259,7 +246,7 @@ public class TerrainPatch extends Geometry {
boolean bottom = utp.getBottomLod() > utp.getNewLod(); boolean bottom = utp.getBottomLod() > utp.getNewLod();
IntBuffer ib = null; IntBuffer ib = null;
if (lodCalculator.usesVariableLod()) if (useVariableLod)
ib = geomap.writeIndexArrayLodVariable(null, pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod())); ib = geomap.writeIndexArrayLodVariable(null, pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod()));
else else
ib = geomap.writeIndexArrayLodDiff(null, pow, right, top, left, bottom); ib = geomap.writeIndexArrayLodDiff(null, pow, right, top, left, bottom);
@ -920,19 +907,11 @@ public class TerrainPatch extends Geometry {
protected void setLodBottom(int lodBottom) { protected void setLodBottom(int lodBottom) {
this.lodBottom = lodBottom; this.lodBottom = lodBottom;
} }
public LodCalculator getLodCalculator() { /*public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) {
return lodCalculator;
}
public void setLodCalculator(LodCalculator lodCalculator) {
this.lodCalculator = lodCalculator;
}
public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) {
this.lodCalculatorFactory = lodCalculatorFactory; this.lodCalculatorFactory = lodCalculatorFactory;
setLodCalculator(lodCalculatorFactory.createCalculator(this)); setLodCalculator(lodCalculatorFactory.createCalculator(this));
} }*/
@Override @Override
public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException { public int collideWith(Collidable other, CollisionResults results) throws UnsupportedCollisionException {
@ -1035,8 +1014,8 @@ public class TerrainPatch extends Geometry {
oc.write(stepScale, "stepScale", Vector3f.UNIT_XYZ); oc.write(stepScale, "stepScale", Vector3f.UNIT_XYZ);
oc.write(offset, "offset", Vector3f.UNIT_XYZ); oc.write(offset, "offset", Vector3f.UNIT_XYZ);
oc.write(offsetAmount, "offsetAmount", 0); oc.write(offsetAmount, "offsetAmount", 0);
oc.write(lodCalculator, "lodCalculator", null); //oc.write(lodCalculator, "lodCalculator", null);
oc.write(lodCalculatorFactory, "lodCalculatorFactory", null); //oc.write(lodCalculatorFactory, "lodCalculatorFactory", null);
oc.write(lodEntropy, "lodEntropy", null); oc.write(lodEntropy, "lodEntropy", null);
oc.write(geomap, "geomap", null); oc.write(geomap, "geomap", null);
@ -1053,9 +1032,9 @@ public class TerrainPatch extends Geometry {
stepScale = (Vector3f) ic.readSavable("stepScale", Vector3f.UNIT_XYZ); stepScale = (Vector3f) ic.readSavable("stepScale", Vector3f.UNIT_XYZ);
offset = (Vector2f) ic.readSavable("offset", Vector3f.UNIT_XYZ); offset = (Vector2f) ic.readSavable("offset", Vector3f.UNIT_XYZ);
offsetAmount = ic.readFloat("offsetAmount", 0); offsetAmount = ic.readFloat("offsetAmount", 0);
lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator()); //lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
lodCalculator.setTerrainPatch(this); //lodCalculator.setTerrainPatch(this);
lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null); //lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null);
lodEntropy = ic.readFloatArray("lodEntropy", null); lodEntropy = ic.readFloatArray("lodEntropy", null);
geomap = (LODGeomap) ic.readSavable("geomap", null); geomap = (LODGeomap) ic.readSavable("geomap", null);
@ -1077,7 +1056,7 @@ public class TerrainPatch extends Geometry {
clone.offsetAmount = offsetAmount; clone.offsetAmount = offsetAmount;
//clone.lodCalculator = lodCalculator.clone(); //clone.lodCalculator = lodCalculator.clone();
//clone.lodCalculator.setTerrainPatch(clone); //clone.lodCalculator.setTerrainPatch(clone);
clone.setLodCalculator(lodCalculatorFactory.clone()); //clone.setLodCalculator(lodCalculatorFactory.clone());
clone.geomap = new LODGeomap(size, geomap.getHeightData()); clone.geomap = new LODGeomap(size, geomap.getHeightData());
clone.setLocalTranslation(getLocalTranslation().clone()); clone.setLocalTranslation(getLocalTranslation().clone());
Mesh m = clone.geomap.createMesh(clone.stepScale, Vector2f.UNIT_XY, clone.offset, clone.offsetAmount, clone.totalSize, false); Mesh m = clone.geomap.createMesh(clone.stepScale, Vector2f.UNIT_XY, clone.offset, clone.offsetAmount, clone.totalSize, false);

@ -57,8 +57,9 @@ import com.jme3.scene.Spatial;
import com.jme3.scene.debug.WireBox; import com.jme3.scene.debug.WireBox;
import com.jme3.terrain.ProgressMonitor; import com.jme3.terrain.ProgressMonitor;
import com.jme3.terrain.Terrain; import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculatorFactory; import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodDistanceCalculatorFactory; import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.SimpleLodThreshold;
import com.jme3.terrain.geomipmap.picking.BresenhamTerrainPicker; import com.jme3.terrain.geomipmap.picking.BresenhamTerrainPicker;
import com.jme3.terrain.geomipmap.picking.TerrainPickData; import com.jme3.terrain.geomipmap.picking.TerrainPickData;
import com.jme3.terrain.geomipmap.picking.TerrainPicker; import com.jme3.terrain.geomipmap.picking.TerrainPicker;
@ -109,7 +110,8 @@ public class TerrainQuad extends Node implements Terrain {
protected int quadrant = 1; // 1=upper left, 2=lower left, 3=upper right, 4=lower right protected int quadrant = 1; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
protected LodCalculatorFactory lodCalculatorFactory; //protected LodCalculatorFactory lodCalculatorFactory;
//protected LodCalculator lodCalculator;
protected List<Vector3f> lastCameraLocations; // used for LOD calc protected List<Vector3f> lastCameraLocations; // used for LOD calc
private boolean lodCalcRunning = false; private boolean lodCalcRunning = false;
@ -139,55 +141,55 @@ public class TerrainQuad extends Node implements Terrain {
} }
public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) { public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {
this(name, patchSize, totalSize, heightMap, null); this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);
}
public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap, LodCalculatorFactory lodCalculatorFactory) {
this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap, lodCalculatorFactory);
} }
public TerrainQuad(String name, int patchSize, int size, int totalSize, float[] heightMap, LodCalculatorFactory lodCalculatorFactory) { public TerrainQuad(String name, int patchSize, int quadSize, int totalSize, float[] heightMap) {
this(name, patchSize, size, Vector3f.UNIT_XYZ, heightMap, totalSize, new Vector2f(), 0, lodCalculatorFactory); this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);
}
public TerrainQuad(String name, int patchSize, int size, Vector3f scale, float[] heightMap) {
this(name, patchSize, size, scale, heightMap, size, new Vector2f(), 0);
affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2); affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);
fixNormalEdges(affectedAreaBBox); fixNormalEdges(affectedAreaBBox);
addControl(new NormalRecalcControl(this)); addControl(new NormalRecalcControl(this));
} }
public TerrainQuad(String name, int patchSize, int size, Vector3f scale, float[] heightMap, LodCalculatorFactory lodCalculatorFactory) { public TerrainQuad(String name, int patchSize, int totalSize, int quadSize, Vector3f scale, float[] heightMap) {
this(name, patchSize, size, scale, heightMap, size, new Vector2f(), 0, lodCalculatorFactory); this(name, patchSize, quadSize, scale, heightMap, totalSize, new Vector2f(), 0);
affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2); affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);
fixNormalEdges(affectedAreaBBox); fixNormalEdges(affectedAreaBBox);
addControl(new NormalRecalcControl(this)); addControl(new NormalRecalcControl(this));
} }
protected TerrainQuad(String name, int patchSize, int size, protected TerrainQuad(String name, int patchSize, int quadSize,
Vector3f scale, float[] heightMap, int totalSize, Vector3f scale, float[] heightMap, int totalSize,
Vector2f offset, float offsetAmount, Vector2f offset, float offsetAmount)
LodCalculatorFactory lodCalculatorFactory)
{ {
super(name); super(name);
if (heightMap == null) if (heightMap == null)
heightMap = generateDefaultHeightMap(size); heightMap = generateDefaultHeightMap(quadSize);
if (!FastMath.isPowerOfTwo(size - 1)) { if (!FastMath.isPowerOfTwo(quadSize - 1)) {
throw new RuntimeException("size given: " + size + " Terrain quad sizes may only be (2^N + 1)"); throw new RuntimeException("size given: " + quadSize + " Terrain quad sizes may only be (2^N + 1)");
} }
if (FastMath.sqrt(heightMap.length) > size) { if (FastMath.sqrt(heightMap.length) > quadSize) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Heightmap size is larger than the terrain size. Make sure your heightmap image is the same size as the terrain!"); Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Heightmap size is larger than the terrain size. Make sure your heightmap image is the same size as the terrain!");
} }
this.offset = offset; this.offset = offset;
this.offsetAmount = offsetAmount; this.offsetAmount = offsetAmount;
this.totalSize = totalSize; this.totalSize = totalSize;
this.size = size; this.size = quadSize;
this.patchSize = patchSize; this.patchSize = patchSize;
this.stepScale = scale; this.stepScale = scale;
this.lodCalculatorFactory = lodCalculatorFactory; //this.lodCalculatorFactory = lodCalculatorFactory;
//this.lodCalculator = lodCalculator;
split(patchSize, heightMap); split(patchSize, heightMap);
} }
public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) { /*public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) {
if (children != null) { if (children != null) {
for (int i = children.size(); --i >= 0;) { for (int i = children.size(); --i >= 0;) {
Spatial child = children.get(i); Spatial child = children.get(i);
@ -198,7 +200,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
} }
} }
} }*/
/** /**
@ -215,8 +217,8 @@ public class TerrainQuad extends Node implements Terrain {
* This will perform the geometry calculation in a background thread and * This will perform the geometry calculation in a background thread and
* do the actual update on the opengl thread. * do the actual update on the opengl thread.
*/ */
public void update(List<Vector3f> locations) { public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
updateLOD(locations); updateLOD(locations, lodCalculator);
} }
/** /**
@ -235,7 +237,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
// do all of the LOD calculations // do all of the LOD calculations
protected void updateLOD(List<Vector3f> locations) { protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
// update any existing ones that need updating // update any existing ones that need updating
updateQuadLODs(); updateQuadLODs();
@ -261,7 +263,7 @@ public class TerrainQuad extends Node implements Terrain {
if (executor == null) if (executor == null)
executor = createExecutorService(); executor = createExecutorService();
UpdateLOD updateLodThread = new UpdateLOD(locations); UpdateLOD updateLodThread = new UpdateLOD(locations, lodCalculator);
executor.execute(updateLodThread); executor.execute(updateLodThread);
} }
@ -359,9 +361,11 @@ public class TerrainQuad extends Node implements Terrain {
*/ */
private class UpdateLOD implements Runnable { private class UpdateLOD implements Runnable {
private List<Vector3f> camLocations; private List<Vector3f> camLocations;
private LodCalculator lodCalculator;
UpdateLOD(List<Vector3f> location) { UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
camLocations = location; this.camLocations = camLocations;
this.lodCalculator = lodCalculator;
} }
public void run() { public void run() {
@ -375,7 +379,7 @@ public class TerrainQuad extends Node implements Terrain {
// go through each patch and calculate its LOD based on camera distance // go through each patch and calculate its LOD based on camera distance
HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>(); HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
boolean lodChanged = calculateLod(camLocations, updated); // 'updated' gets populated here boolean lodChanged = calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
if (!lodChanged) { if (!lodChanged) {
// not worth updating anything else since no one's LOD changed // not worth updating anything else since no one's LOD changed
@ -387,7 +391,7 @@ public class TerrainQuad extends Node implements Terrain {
fixEdges(updated); // 'updated' can get added to here fixEdges(updated); // 'updated' can get added to here
reIndexPages(updated); reIndexPages(updated, lodCalculator.usesVariableLod());
setUpdateQuadLODs(updated); // set back to main ogl thread setUpdateQuadLODs(updated); // set back to main ogl thread
@ -422,7 +426,7 @@ public class TerrainQuad extends Node implements Terrain {
} }
} }
protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates) { protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) {
boolean lodChanged = false; boolean lodChanged = false;
@ -430,11 +434,11 @@ public class TerrainQuad extends Node implements Terrain {
for (int i = children.size(); --i >= 0;) { for (int i = children.size(); --i >= 0;) {
Spatial child = children.get(i); Spatial child = children.get(i);
if (child instanceof TerrainQuad) { if (child instanceof TerrainQuad) {
boolean b = ((TerrainQuad) child).calculateLod(location, updates); boolean b = ((TerrainQuad) child).calculateLod(location, updates, lodCalculator);
if (b) if (b)
lodChanged = true; lodChanged = true;
} else if (child instanceof TerrainPatch) { } else if (child instanceof TerrainPatch) {
boolean b = ((TerrainPatch) child).calculateLod(location, updates); boolean b = lodCalculator.calculateLod((TerrainPatch) child, location, updates);
if (b) if (b)
lodChanged = true; lodChanged = true;
} }
@ -561,14 +565,14 @@ public class TerrainQuad extends Node implements Terrain {
} }
} }
protected synchronized void reIndexPages(HashMap<String,UpdatedTerrainPatch> updated) { protected synchronized void reIndexPages(HashMap<String,UpdatedTerrainPatch> updated, boolean usesVariableLod) {
if (children != null) { if (children != null) {
for (int i = children.size(); --i >= 0;) { for (int i = children.size(); --i >= 0;) {
Spatial child = children.get(i); Spatial child = children.get(i);
if (child instanceof TerrainQuad) { if (child instanceof TerrainQuad) {
((TerrainQuad) child).reIndexPages(updated); ((TerrainQuad) child).reIndexPages(updated, usesVariableLod);
} else if (child instanceof TerrainPatch) { } else if (child instanceof TerrainPatch) {
((TerrainPatch) child).reIndexGeometry(updated); ((TerrainPatch) child).reIndexGeometry(updated, usesVariableLod);
} }
} }
} }
@ -616,8 +620,8 @@ public class TerrainQuad extends Node implements Terrain {
Vector2f tempOffset = new Vector2f(); Vector2f tempOffset = new Vector2f();
offsetAmount += quarterSize; offsetAmount += quarterSize;
if (lodCalculatorFactory == null) //if (lodCalculator == null)
lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one // lodCalculator = createDefaultLodCalculator(); // set a default one
// 1 upper left of heightmap, lower left quad // 1 upper left of heightmap, lower left quad
float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split); float[] heightBlock1 = createHeightSubBlock(heightMap, 0, 0, split);
@ -632,7 +636,7 @@ public class TerrainQuad extends Node implements Terrain {
TerrainQuad quad1 = new TerrainQuad(getName() + "Quad1", blockSize, TerrainQuad quad1 = new TerrainQuad(getName() + "Quad1", blockSize,
split, stepScale, heightBlock1, totalSize, tempOffset, split, stepScale, heightBlock1, totalSize, tempOffset,
offsetAmount, lodCalculatorFactory); offsetAmount);
quad1.setLocalTranslation(origin1); quad1.setLocalTranslation(origin1);
quad1.quadrant = 1; quad1.quadrant = 1;
this.attachChild(quad1); this.attachChild(quad1);
@ -652,7 +656,7 @@ public class TerrainQuad extends Node implements Terrain {
TerrainQuad quad2 = new TerrainQuad(getName() + "Quad2", blockSize, TerrainQuad quad2 = new TerrainQuad(getName() + "Quad2", blockSize,
split, stepScale, heightBlock2, totalSize, tempOffset, split, stepScale, heightBlock2, totalSize, tempOffset,
offsetAmount, lodCalculatorFactory); offsetAmount);
quad2.setLocalTranslation(origin2); quad2.setLocalTranslation(origin2);
quad2.quadrant = 2; quad2.quadrant = 2;
this.attachChild(quad2); this.attachChild(quad2);
@ -672,7 +676,7 @@ public class TerrainQuad extends Node implements Terrain {
TerrainQuad quad3 = new TerrainQuad(getName() + "Quad3", blockSize, TerrainQuad quad3 = new TerrainQuad(getName() + "Quad3", blockSize,
split, stepScale, heightBlock3, totalSize, tempOffset, split, stepScale, heightBlock3, totalSize, tempOffset,
offsetAmount, lodCalculatorFactory); offsetAmount);
quad3.setLocalTranslation(origin3); quad3.setLocalTranslation(origin3);
quad3.quadrant = 3; quad3.quadrant = 3;
this.attachChild(quad3); this.attachChild(quad3);
@ -692,7 +696,7 @@ public class TerrainQuad extends Node implements Terrain {
TerrainQuad quad4 = new TerrainQuad(getName() + "Quad4", blockSize, TerrainQuad quad4 = new TerrainQuad(getName() + "Quad4", blockSize,
split, stepScale, heightBlock4, totalSize, tempOffset, split, stepScale, heightBlock4, totalSize, tempOffset,
offsetAmount, lodCalculatorFactory); offsetAmount);
quad4.setLocalTranslation(origin4); quad4.setLocalTranslation(origin4);
quad4.quadrant = 4; quad4.quadrant = 4;
this.attachChild(quad4); this.attachChild(quad4);
@ -724,8 +728,8 @@ public class TerrainQuad extends Node implements Terrain {
int halfSize = size >> 1; int halfSize = size >> 1;
int split = (size + 1) >> 1; int split = (size + 1) >> 1;
if (lodCalculatorFactory == null) //if (lodCalculator == null)
lodCalculatorFactory = new LodDistanceCalculatorFactory(); // set a default one // lodCalculator = createDefaultLodCalculator(); // set a default one
offsetAmount += quarterSize; offsetAmount += quarterSize;
@ -748,7 +752,7 @@ public class TerrainQuad extends Node implements Terrain {
this.attachChild(patch1); this.attachChild(patch1);
patch1.setModelBound(new BoundingBox()); patch1.setModelBound(new BoundingBox());
patch1.updateModelBound(); patch1.updateModelBound();
patch1.setLodCalculator(lodCalculatorFactory); //patch1.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch1); //TangentBinormalGenerator.generate(patch1);
// 2 lower left // 2 lower left
@ -770,7 +774,7 @@ public class TerrainQuad extends Node implements Terrain {
this.attachChild(patch2); this.attachChild(patch2);
patch2.setModelBound(new BoundingBox()); patch2.setModelBound(new BoundingBox());
patch2.updateModelBound(); patch2.updateModelBound();
patch2.setLodCalculator(lodCalculatorFactory); //patch2.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch2); //TangentBinormalGenerator.generate(patch2);
// 3 upper right // 3 upper right
@ -792,7 +796,7 @@ public class TerrainQuad extends Node implements Terrain {
this.attachChild(patch3); this.attachChild(patch3);
patch3.setModelBound(new BoundingBox()); patch3.setModelBound(new BoundingBox());
patch3.updateModelBound(); patch3.updateModelBound();
patch3.setLodCalculator(lodCalculatorFactory); //patch3.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch3); //TangentBinormalGenerator.generate(patch3);
// 4 lower right // 4 lower right
@ -814,7 +818,7 @@ public class TerrainQuad extends Node implements Terrain {
this.attachChild(patch4); this.attachChild(patch4);
patch4.setModelBound(new BoundingBox()); patch4.setModelBound(new BoundingBox());
patch4.updateModelBound(); patch4.updateModelBound();
patch4.setLodCalculator(lodCalculatorFactory); //patch4.setLodCalculator(lodCalculator);
//TangentBinormalGenerator.generate(patch4); //TangentBinormalGenerator.generate(patch4);
} }
@ -1634,7 +1638,8 @@ public class TerrainQuad extends Node implements Terrain {
offsetAmount = c.readFloat("offsetAmount", 0); offsetAmount = c.readFloat("offsetAmount", 0);
quadrant = c.readInt("quadrant", 0); quadrant = c.readInt("quadrant", 0);
totalSize = c.readInt("totalSize", 0); totalSize = c.readInt("totalSize", 0);
lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null); //lodCalculator = (LodCalculator) c.readSavable("lodCalculator", createDefaultLodCalculator());
//lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);
if ( !(getParent() instanceof TerrainQuad) ) { if ( !(getParent() instanceof TerrainQuad) ) {
BoundingBox all = new BoundingBox(getWorldTranslation(), totalSize, totalSize, totalSize); BoundingBox all = new BoundingBox(getWorldTranslation(), totalSize, totalSize, totalSize);
@ -1653,7 +1658,8 @@ public class TerrainQuad extends Node implements Terrain {
c.write(offset, "offset", new Vector2f(0,0)); c.write(offset, "offset", new Vector2f(0,0));
c.write(offsetAmount, "offsetAmount", 0); c.write(offsetAmount, "offsetAmount", 0);
c.write(quadrant, "quadrant", 0); c.write(quadrant, "quadrant", 0);
c.write(lodCalculatorFactory, "lodCalculatorFactory", null); //c.write(lodCalculatorFactory, "lodCalculatorFactory", null);
//c.write(lodCalculator, "lodCalculator", null);
} }
@Override @Override
@ -1671,7 +1677,9 @@ public class TerrainQuad extends Node implements Terrain {
quadClone.offset = offset.clone(); quadClone.offset = offset.clone();
quadClone.offsetAmount = offsetAmount; quadClone.offsetAmount = offsetAmount;
quadClone.quadrant = quadrant; quadClone.quadrant = quadrant;
quadClone.lodCalculatorFactory = lodCalculatorFactory.clone(); //quadClone.lodCalculatorFactory = lodCalculatorFactory.clone();
//quadClone.lodCalculator = lodCalculator.clone();
TerrainLodControl lodControl = quadClone.getControl(TerrainLodControl.class); TerrainLodControl lodControl = quadClone.getControl(TerrainLodControl.class);
if (lodControl != null && !(getParent() instanceof TerrainQuad)) { if (lodControl != null && !(getParent() instanceof TerrainQuad)) {
lodControl.setTerrain(quadClone); // set println in controller update to see if it is updating lodControl.setTerrain(quadClone); // set println in controller update to see if it is updating
@ -1682,8 +1690,7 @@ public class TerrainQuad extends Node implements Terrain {
return quadClone; return quadClone;
} }
public int getMaxLod() { public int getMaxLod() {
if (maxLod < 0) if (maxLod < 0)
maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide
@ -1699,7 +1706,6 @@ public class TerrainQuad extends Node implements Terrain {
return totalSize; return totalSize;
} }
public float[] getHeightMap() { public float[] getHeightMap() {
float[] hm = null; float[] hm = null;

@ -50,27 +50,23 @@ import java.util.List;
*/ */
public class DistanceLodCalculator implements LodCalculator { public class DistanceLodCalculator implements LodCalculator {
private TerrainPatch terrainPatch; private int size; // size of a terrain patch
private LodThreshold lodThresholdCalculator; private float lodMultiplier = 2;
public DistanceLodCalculator() { public DistanceLodCalculator() {
} }
public DistanceLodCalculator(LodThreshold lodThresholdCalculator) { public DistanceLodCalculator(int patchSize, float multiplier) {
this.lodThresholdCalculator = lodThresholdCalculator; this.size = patchSize;
this.lodMultiplier = multiplier;
} }
public DistanceLodCalculator(TerrainPatch terrainPatch, LodThreshold lodThresholdCalculator) { public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
this.terrainPatch = terrainPatch; float distance = getCenterLocation(terrainPatch).distance(locations.get(0));
this.lodThresholdCalculator = lodThresholdCalculator;
}
public boolean calculateLod(List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
float distance = getCenterLocation().distance(locations.get(0));
// go through each lod level to find the one we are in // go through each lod level to find the one we are in
for (int i = 0; i <= terrainPatch.getMaxLod(); i++) { for (int i = 0; i <= terrainPatch.getMaxLod(); i++) {
if (distance < lodThresholdCalculator.getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScale().x || i == terrainPatch.getMaxLod()) { if (distance < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScale().x || i == terrainPatch.getMaxLod()) {
boolean reIndexNeeded = false; boolean reIndexNeeded = false;
if (i != terrainPatch.getLod()) { if (i != terrainPatch.getLod()) {
reIndexNeeded = true; reIndexNeeded = true;
@ -94,49 +90,67 @@ public class DistanceLodCalculator implements LodCalculator {
return false; return false;
} }
public Vector3f getCenterLocation() { protected Vector3f getCenterLocation(TerrainPatch terrainPatch) {
Vector3f loc = terrainPatch.getWorldTranslation().clone(); Vector3f loc = terrainPatch.getWorldTranslation().clone();
loc.x += terrainPatch.getSize()*terrainPatch.getWorldScale().x / 2; loc.x += terrainPatch.getSize()*terrainPatch.getWorldScale().x / 2;
loc.z += terrainPatch.getSize()*terrainPatch.getWorldScale().z / 2; loc.z += terrainPatch.getSize()*terrainPatch.getWorldScale().z / 2;
return loc; return loc;
} }
public void setTerrainPatch(TerrainPatch terrainPatch) {
this.terrainPatch = terrainPatch;
}
protected LodThreshold getLodThreshold() {
return lodThresholdCalculator;
}
protected void setLodThreshold(LodThreshold lodThresholdCalculator) {
this.lodThresholdCalculator = lodThresholdCalculator;
}
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write(lodThresholdCalculator, "lodThresholdCalculator", null); oc.write(size, "patchSize", 32);
oc.write(lodMultiplier, "lodMultiplier", 32);
} }
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
lodThresholdCalculator = (LodThreshold) ic.readSavable("lodThresholdCalculator", null); size = ic.readInt("patchSize", 32);
lodMultiplier = ic.readFloat("lodMultiplier", 2.7f);
} }
@Override @Override
public LodCalculator clone() { public LodCalculator clone() {
DistanceLodCalculator clone = new DistanceLodCalculator(); DistanceLodCalculator clone = new DistanceLodCalculator();
clone.lodThresholdCalculator = lodThresholdCalculator.clone();
return clone; return clone;
} }
@Override @Override
public String toString() { public String toString() {
return "DistanceLodCalculator " + lodThresholdCalculator.toString(); return "DistanceLodCalculator "+size+"*"+lodMultiplier;
} }
/**
* Gets the camera distance where the LOD level will change
*/
protected float getLodDistanceThreshold() {
return size*lodMultiplier;
}
/**
* Does this calculator require the terrain to have the difference of
* LOD levels of neighbours to be more than 1.
*/
public boolean usesVariableLod() { public boolean usesVariableLod() {
return false; return false;
} }
public float getLodMultiplier() {
return lodMultiplier;
}
public void setLodMultiplier(float lodMultiplier) {
this.lodMultiplier = lodMultiplier;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
} }

@ -47,9 +47,8 @@ import java.util.List;
*/ */
public interface LodCalculator extends Savable, Cloneable { public interface LodCalculator extends Savable, Cloneable {
public void setTerrainPatch(TerrainPatch terrainPatch); public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String,UpdatedTerrainPatch> updates);
public boolean calculateLod(List<Vector3f> locations, HashMap<String,UpdatedTerrainPatch> updates);
public LodCalculator clone(); public LodCalculator clone();
/** /**

@ -39,6 +39,7 @@ import com.jme3.terrain.geomipmap.TerrainPatch;
* Creates LOD Calculator objects for the terrain patches. * Creates LOD Calculator objects for the terrain patches.
* *
* @author Brent Owens * @author Brent Owens
* @deprecated phasing this out
*/ */
public interface LodCalculatorFactory extends Savable, Cloneable { public interface LodCalculatorFactory extends Savable, Cloneable {

@ -42,6 +42,7 @@ import java.io.IOException;
/** /**
* *
* @author bowens * @author bowens
* @deprecated phasing out
*/ */
public class LodDistanceCalculatorFactory implements LodCalculatorFactory { public class LodDistanceCalculatorFactory implements LodCalculatorFactory {
@ -61,9 +62,7 @@ public class LodDistanceCalculatorFactory implements LodCalculatorFactory {
} }
public LodCalculator createCalculator(TerrainPatch terrainPatch) { public LodCalculator createCalculator(TerrainPatch terrainPatch) {
if (lodThreshold == null) return new DistanceLodCalculator();
lodThreshold = new SimpleLodThreshold(terrainPatch.getSize(), lodThresholdSize);
return new DistanceLodCalculator(terrainPatch, lodThreshold);
} }
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {

@ -42,6 +42,7 @@ import java.io.IOException;
* TODO: Make it work with multiple cameras * TODO: Make it work with multiple cameras
* TODO: Fix the cracks when the lod differences are greater than 1 * TODO: Fix the cracks when the lod differences are greater than 1
* for two adjacent blocks. * for two adjacent blocks.
* @deprecated phasing out
*/ */
public class LodPerspectiveCalculatorFactory implements LodCalculatorFactory { public class LodPerspectiveCalculatorFactory implements LodCalculatorFactory {
@ -59,7 +60,6 @@ public class LodPerspectiveCalculatorFactory implements LodCalculatorFactory {
public LodCalculator createCalculator(TerrainPatch terrainPatch) { public LodCalculator createCalculator(TerrainPatch terrainPatch) {
PerspectiveLodCalculator p = new PerspectiveLodCalculator(cam, pixelError); PerspectiveLodCalculator p = new PerspectiveLodCalculator(cam, pixelError);
p.setTerrainPatch(terrainPatch);
return p; return p;
} }

@ -45,10 +45,10 @@ import com.jme3.export.Savable;
*/ */
public interface LodThreshold extends Savable, Cloneable { public interface LodThreshold extends Savable, Cloneable {
/** /**
* A distance of how far between each LOD threshold. * A distance of how far between each LOD threshold.
*/ */
public float getLodDistanceThreshold(); public float getLodDistanceThreshold();
public LodThreshold clone(); public LodThreshold clone();
} }

@ -50,6 +50,8 @@ public class PerspectiveLodCalculator implements LodCalculator {
private float[] entropyDistances; private float[] entropyDistances;
private float pixelError; private float pixelError;
public PerspectiveLodCalculator() {}
public PerspectiveLodCalculator(Camera cam, float pixelError){ public PerspectiveLodCalculator(Camera cam, float pixelError){
this.cam = cam; this.cam = cam;
this.pixelError = pixelError; this.pixelError = pixelError;
@ -71,12 +73,12 @@ public class PerspectiveLodCalculator implements LodCalculator {
float T = (2f * pixelLimit) / v_res; float T = (2f * pixelLimit) / v_res;
return A / T; return A / T;
} }
public void setTerrainPatch(TerrainPatch terrainPatch) { public boolean calculateLod(List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
patch = terrainPatch; return calculateLod(patch, locations, updates);
} }
public boolean calculateLod(List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) { public boolean calculateLod(TerrainPatch terrainPatch, List<Vector3f> locations, HashMap<String, UpdatedTerrainPatch> updates) {
if (entropyDistances == null){ if (entropyDistances == null){
// compute entropy distances // compute entropy distances
float[] lodEntropies = patch.getLodEntropies(); float[] lodEntropies = patch.getLodEntropies();
@ -87,7 +89,7 @@ public class PerspectiveLodCalculator implements LodCalculator {
} }
} }
Vector3f patchPos = getCenterLocation(); Vector3f patchPos = getCenterLocation(patch);
// vector from camera to patch // vector from camera to patch
//Vector3f toPatchDir = locations.get(0).subtract(patchPos).normalizeLocal(); //Vector3f toPatchDir = locations.get(0).subtract(patchPos).normalizeLocal();
@ -120,7 +122,7 @@ public class PerspectiveLodCalculator implements LodCalculator {
return false; return false;
} }
public Vector3f getCenterLocation() { public Vector3f getCenterLocation(TerrainPatch patch) {
Vector3f loc = patch.getWorldTranslation().clone(); Vector3f loc = patch.getWorldTranslation().clone();
loc.x += patch.getSize() / 2; loc.x += patch.getSize() / 2;
loc.z += patch.getSize() / 2; loc.z += patch.getSize() / 2;
@ -146,4 +148,18 @@ public class PerspectiveLodCalculator implements LodCalculator {
return true; return true;
} }
public float getPixelError() {
return pixelError;
}
public void setPixelError(float pixelError) {
this.pixelError = pixelError;
}
public void setCam(Camera cam) {
this.cam = cam;
}
} }

@ -36,6 +36,8 @@ import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter; import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter; import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule; import com.jme3.export.OutputCapsule;
import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.TerrainQuad;
import java.io.IOException; import java.io.IOException;
@ -49,17 +51,21 @@ import java.io.IOException;
*/ */
public class SimpleLodThreshold implements LodThreshold { public class SimpleLodThreshold implements LodThreshold {
private int size; // size of a terrain patch private int size; // size of a terrain patch
private float lodMultiplier = 2; private float lodMultiplier = 2;
public SimpleLodThreshold() { public SimpleLodThreshold() {
}
public SimpleLodThreshold(Terrain terrain) {
if (terrain instanceof TerrainQuad)
this.size = ((TerrainQuad)terrain).getPatchSize();
} }
public SimpleLodThreshold(int patchSize, float lodMultiplier) { public SimpleLodThreshold(int patchSize, float lodMultiplier) {
this.size = patchSize; this.size = patchSize;
} }
public float getLodMultiplier() { public float getLodMultiplier() {
return lodMultiplier; return lodMultiplier;
@ -78,9 +84,9 @@ public class SimpleLodThreshold implements LodThreshold {
} }
public float getLodDistanceThreshold() { public float getLodDistanceThreshold() {
return size*lodMultiplier; return size*lodMultiplier;
} }
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);

@ -22,6 +22,7 @@ import com.jme3.renderer.Camera;
import com.jme3.terrain.geomipmap.TerrainGrid; import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener; import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.FractalHeightMapGrid; import com.jme3.terrain.heightmap.FractalHeightMapGrid;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture.WrapMode;
@ -145,6 +146,7 @@ public class TerrainFractalGridTest extends SimpleApplication {
List<Camera> cameras = new ArrayList<Camera>(); List<Camera> cameras = new ArrayList<Camera>();
cameras.add(this.getCamera()); cameras.add(this.getCamera());
TerrainLodControl control = new TerrainLodControl(this.terrain, cameras); TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control); this.terrain.addControl(control);
final BulletAppState bulletAppState = new BulletAppState(); final BulletAppState bulletAppState = new BulletAppState();

@ -26,6 +26,7 @@ import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener; import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.FractalHeightMapGrid; import com.jme3.terrain.heightmap.FractalHeightMapGrid;
import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid; import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
import com.jme3.terrain.heightmap.Namer; import com.jme3.terrain.heightmap.Namer;
@ -158,6 +159,7 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
List<Camera> cameras = new ArrayList<Camera>(); List<Camera> cameras = new ArrayList<Camera>();
cameras.add(this.getCamera()); cameras.add(this.getCamera());
TerrainLodControl control = new TerrainLodControl(this.terrain, cameras); TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control); this.terrain.addControl(control);
final BulletAppState bulletAppState = new BulletAppState(); final BulletAppState bulletAppState = new BulletAppState();

@ -19,6 +19,7 @@ import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener; import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid; import com.jme3.terrain.heightmap.ImageBasedHeightMapGrid;
import com.jme3.terrain.heightmap.Namer; import com.jme3.terrain.heightmap.Namer;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
@ -106,6 +107,7 @@ public class TerrainGridTest extends SimpleApplication {
this.rootNode.attachChild(this.terrain); this.rootNode.attachChild(this.terrain);
TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera()); TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control); this.terrain.addControl(control);
final BulletAppState bulletAppState = new BulletAppState(); final BulletAppState bulletAppState = new BulletAppState();

@ -47,6 +47,8 @@ import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.SimpleLodThreshold;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture.WrapMode;
@ -155,8 +157,9 @@ public class TerrainTest extends SimpleApplication {
* The total size is up to you. At 1025 it ran fine for me (200+FPS), however at * 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... * size=2049, it got really slow. But that is a jump from 2 million to 8 million triangles...
*/ */
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
TerrainLodControl control = new TerrainLodControl(terrain, getCamera()); TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain.addControl(control); terrain.addControl(control);
terrain.setMaterial(matRock); terrain.setMaterial(matRock);
terrain.setLocalTranslation(0, -100, 0); terrain.setLocalTranslation(0, -100, 0);

@ -50,6 +50,7 @@ import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory;
@ -96,7 +97,6 @@ public class TerrainTestAdvanced extends SimpleApplication {
// TERRAIN TEXTURE material // TERRAIN TEXTURE material
matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md"); matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
matTerrain.setBoolean("useTriPlanarMapping", false); matTerrain.setBoolean("useTriPlanarMapping", false);
matTerrain.setBoolean("WardIso", true);
// ALPHA map (for splat textures) // ALPHA map (for splat textures)
matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png")); matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png"));
@ -105,18 +105,17 @@ public class TerrainTestAdvanced extends SimpleApplication {
// HEIGHTMAP image (for the terrain heightmap) // HEIGHTMAP image (for the terrain heightmap)
Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
// GRASS texture // GRASS texture
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat); grass.setWrap(WrapMode.Repeat);
matTerrain.setTexture("DiffuseMap", grass); matTerrain.setTexture("DiffuseMap_1", grass);
matTerrain.setFloat("DiffuseMap_0_scale", grassScale); matTerrain.setFloat("DiffuseMap_1_scale", grassScale);
// DIRT texture // DIRT texture
Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat); dirt.setWrap(WrapMode.Repeat);
matTerrain.setTexture("DiffuseMap_1", dirt); matTerrain.setTexture("DiffuseMap", dirt);
matTerrain.setFloat("DiffuseMap_1_scale", dirtScale); matTerrain.setFloat("DiffuseMap_0_scale", dirtScale);
// ROCK texture // ROCK texture
Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
@ -143,7 +142,7 @@ public class TerrainTestAdvanced extends SimpleApplication {
normalMap1.setWrap(WrapMode.Repeat); normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat); normalMap2.setWrap(WrapMode.Repeat);
matTerrain.setTexture("NormalMap", normalMap0); matTerrain.setTexture("NormalMap", normalMap2);
matTerrain.setTexture("NormalMap_1", normalMap2); matTerrain.setTexture("NormalMap_1", normalMap2);
matTerrain.setTexture("NormalMap_2", normalMap2); matTerrain.setTexture("NormalMap_2", normalMap2);
matTerrain.setTexture("NormalMap_4", normalMap2); matTerrain.setTexture("NormalMap_4", normalMap2);
@ -158,10 +157,9 @@ public class TerrainTestAdvanced extends SimpleApplication {
// CREATE HEIGHTMAP // CREATE HEIGHTMAP
AbstractHeightMap heightmap = null; AbstractHeightMap heightmap = null;
try { try {
//heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3); heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 0.5f);
heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 1f);
heightmap.load(); heightmap.load();
heightmap.smooth(0.9f, 1);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -178,6 +176,7 @@ public class TerrainTestAdvanced extends SimpleApplication {
*/ */
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
TerrainLodControl control = new TerrainLodControl(terrain, getCamera()); TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain.addControl(control); terrain.addControl(control);
terrain.setMaterial(matTerrain); terrain.setMaterial(matTerrain);
terrain.setModelBound(new BoundingBox()); terrain.setModelBound(new BoundingBox());
@ -185,17 +184,17 @@ public class TerrainTestAdvanced extends SimpleApplication {
terrain.setLocalTranslation(0, -100, 0); terrain.setLocalTranslation(0, -100, 0);
terrain.setLocalScale(1f, 1f, 1f); terrain.setLocalScale(1f, 1f, 1f);
rootNode.attachChild(terrain); rootNode.attachChild(terrain);
Material debugMat = assetManager.loadMaterial("Common/Materials/VertexColor.j3m");
//terrain.generateDebugTangents(debugMat);
DirectionalLight light = new DirectionalLight(); DirectionalLight light = new DirectionalLight();
light.setDirection((new Vector3f(-0.5f, -1f, -0.5f)).normalize()); light.setDirection((new Vector3f(-0.25f, -1f, -0.25f)).normalize());
rootNode.addLight(light); rootNode.addLight(light);
AmbientLight ambLight = new AmbientLight();
ambLight.setColor(new ColorRGBA(1f, 1f, 0.8f, 0.2f));
rootNode.addLight(ambLight);
cam.setLocation(new Vector3f(0, 10, -10)); cam.setLocation(new Vector3f(0, 10, -10));
cam.lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y); cam.lookAtDirection(new Vector3f(0, -1.5f, -1).normalizeLocal(), Vector3f.UNIT_Y);
flyCam.setMoveSpeed(400);
} }
public void loadHintText() { public void loadHintText() {

@ -60,6 +60,7 @@ import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture.WrapMode;
@ -134,6 +135,7 @@ public class TerrainTestCollision extends SimpleApplication {
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap()); terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
TerrainLodControl control = new TerrainLodControl(terrain, getCamera()); TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain.addControl(control); terrain.addControl(control);
terrain.setMaterial(matRock); terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2)); terrain.setLocalScale(new Vector3f(2, 2, 2));

@ -54,6 +54,7 @@ import com.jme3.scene.shape.Sphere;
import com.jme3.terrain.geomipmap.TerrainGrid; import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.FractalHeightMapGrid; import com.jme3.terrain.heightmap.FractalHeightMapGrid;
import com.jme3.terrain.heightmap.ImageBasedHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap;
@ -317,6 +318,7 @@ public class TerrainTestModifyHeight extends SimpleApplication {
// CREATE THE TERRAIN // CREATE THE TERRAIN
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap()); terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
TerrainLodControl control = new TerrainLodControl(terrain, getCamera()); TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain.addControl(control); terrain.addControl(control);
terrain.setMaterial(matTerrain); terrain.setMaterial(matTerrain);
terrain.setLocalTranslation(0, -100, 0); terrain.setLocalTranslation(0, -100, 0);

@ -47,6 +47,7 @@ import com.jme3.scene.Node;
import com.jme3.terrain.Terrain; import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
@ -159,6 +160,7 @@ public class TerrainTestReadWrite extends SimpleApplication {
// create the terrain as normal, and give it a control for LOD management // create the terrain as normal, and give it a control for LOD management
TerrainQuad terrainQuad = new TerrainQuad("terrain", 65, 129, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations TerrainQuad terrainQuad = new TerrainQuad("terrain", 65, 129, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
TerrainLodControl control = new TerrainLodControl(terrainQuad, getCamera()); TerrainLodControl control = new TerrainLodControl(terrainQuad, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrainQuad.addControl(control); terrainQuad.addControl(control);
terrainQuad.setMaterial(matTerrain); terrainQuad.setMaterial(matTerrain);
terrainQuad.setLocalTranslation(0, -100, 0); terrainQuad.setLocalTranslation(0, -100, 0);

Loading…
Cancel
Save