* refactored terrain lod to move most of the lod code to the control

* lodDistanceCalculator no longer queries the world transforms of the terrain on the background thread

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9269 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 13 years ago
parent 198979bf5a
commit a1249b551d
  1. 11
      engine/src/terrain/com/jme3/terrain/Terrain.java
  2. 72
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java
  3. 185
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
  4. 19
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
  5. 203
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
  6. 204
      engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java
  7. 8
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java
  8. 3
      engine/src/test/jme3test/terrain/TerrainFractalGridTest.java
  9. 5
      engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java
  10. 3
      engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java
  11. 3
      engine/src/test/jme3test/terrain/TerrainGridTest.java
  12. 3
      engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java

@ -125,17 +125,6 @@ public interface Terrain {
*/
public int getMaxLod();
/**
* Called by an LodControl.
* Calculates the level of detail of the terrain and adjusts its geometry.
* This is where the Terrain's LOD algorithm will change the detail of
* the terrain based on how far away this position is from the particular
* terrain patch.
* @param location the Camera's location. A list of one camera location is normal
* if you just have one camera in your scene.
*/
public void update(List<Vector3f> location, LodCalculator lodCalculator);
/**
* Lock or unlock the meshes of this terrain.
* Locked meshes are un-editable but have better performance.

@ -51,6 +51,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -111,9 +114,10 @@ public class TerrainGrid extends TerrainQuad {
protected Set<TerrainGridListener> listeners = new HashSet<TerrainGridListener>();
protected Material material;
protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(16);
private int cellsLoaded = 0;
private int[] gridOffset;
private boolean runOnce = false;
protected int cellsLoaded = 0;
protected int[] gridOffset;
protected boolean runOnce = false;
protected ExecutorService cacheExecutor;
protected class UpdateQuadCache implements Runnable {
@ -266,44 +270,6 @@ public class TerrainGrid extends TerrainQuad {
}
/**
* @deprecated not needed to be called any more, handled automatically
*/
public void initialize(Vector3f location) {
if (this.material == null) {
throw new RuntimeException("Material must be set prior to call of initialize");
}
Vector3f camCell = this.getCamCell(location);
this.updateChildren(camCell);
for (TerrainGridListener l : this.listeners) {
l.gridMoved(camCell);
}
}
@Override
public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
// 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.isEmpty() ? Vector3f.ZERO.clone() : locations.get(0);
Vector3f camCell = this.getCamCell(cam); // get the grid index value of where the camera is (ie. 2,1)
if (cellsLoaded > 1) { // Check if cells are updated before updating gridoffset.
gridOffset[0] = Math.round(camCell.x * (size / 2));
gridOffset[1] = Math.round(camCell.z * (size / 2));
cellsLoaded = 0;
}
if (camCell.x != this.currentCamCell.x || camCell.z != currentCamCell.z || !runOnce) {
// if the camera has moved into a new cell, load new terrain into the visible 4 center quads
this.updateChildren(camCell);
for (TerrainGridListener l : this.listeners) {
l.gridMoved(camCell);
}
}
runOnce = true;
super.update(locations, lodCalculator);
}
public Vector3f getCamCell(Vector3f location) {
Vector3f tile = getTileCell(location);
Vector3f offsetHalf = new Vector3f(-0.5f, 0, -0.5f);
@ -361,13 +327,6 @@ public class TerrainGrid extends TerrainQuad {
}
}
@Deprecated
/**
* @Deprecated, use updateChildren
*/
protected void updateChildrens(Vector3f camCell) {
updateChildren(camCell);
}
/**
* Called when the camera has moved into a new cell. We need to
@ -417,12 +376,12 @@ public class TerrainGrid extends TerrainQuad {
// ---------------------------------------------------
// ---------------------------------------------------
if (executor == null) {
if (cacheExecutor == null) {
// use the same executor as the LODControl
executor = createExecutorService();
cacheExecutor = createExecutorService();
}
executor.submit(new UpdateQuadCache(camCell));
cacheExecutor.submit(new UpdateQuadCache(camCell));
this.currentCamCell = camCell;
}
@ -480,6 +439,17 @@ public class TerrainGrid extends TerrainQuad {
return terrain.getMaterial(worldLocation);
}
protected ExecutorService createExecutorService() {
return Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread th = new Thread(r);
th.setName("jME Terrain Thread");
th.setDaemon(true);
return th;
}
});
}
@Override
public void read(JmeImporter im) throws IOException {
super.read(im);

@ -48,7 +48,11 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
/**
* Tells the terrain to update its Level of Detail.
@ -73,6 +77,15 @@ public class TerrainLodControl extends AbstractControl {
private LodCalculator lodCalculator;
private boolean hasResetLod = false; // used when enabled is set to false
private HashMap<String,UpdatedTerrainPatch> updatedPatches;
private final Object updatePatchesLock = new Object();
protected List<Vector3f> lastCameraLocations; // used for LOD calc
private boolean lodCalcRunning = false;
private int lodOffCount = 0;
protected ExecutorService executor;
public TerrainLodControl() {
}
@ -98,10 +111,16 @@ public class TerrainLodControl extends AbstractControl {
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
@Override
public void update(float tpf) {
controlUpdate(tpf);
protected ExecutorService createExecutorService() {
return Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread th = new Thread(r);
th.setName("jME Terrain Thread");
th.setDaemon(true);
return th;
}
});
}
@Override
@ -126,10 +145,116 @@ public class TerrainLodControl extends AbstractControl {
cameraLocations.add(c.getLocation());
}
}
terrain.update(cameraLocations, lodCalculator);
updateLOD(cameraLocations, lodCalculator);
}
}
// do all of the LOD calculations
protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
// update any existing ones that need updating
updateQuadLODs();
if (lodCalculator.isLodOff()) {
// we want to calculate the base lod at least once
if (lodOffCount == 1)
return;
else
lodOffCount++;
} else
lodOffCount = 0;
if (lastCameraLocations != null) {
if (lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())
return; // don't update if in same spot
else
lastCameraLocations = cloneVectorList(locations);
}
else {
lastCameraLocations = cloneVectorList(locations);
return;
}
if (isLodCalcRunning()) {
return;
}
//if (getParent() instanceof TerrainQuad) {
// return; // we just want the root quad to perform this.
//}
if (executor == null)
executor = createExecutorService();
TerrainQuad terrain = (TerrainQuad)getSpatial();
terrain.cacheTerrainTransforms();// cache the terrain's world transforms so they can be accessed on the separate thread safely
UpdateLOD updateLodThread = new UpdateLOD(locations, lodCalculator);
executor.execute(updateLodThread);
}
private void setUpdateQuadLODs(HashMap<String,UpdatedTerrainPatch> updated) {
synchronized (updatePatchesLock) {
updatedPatches = updated;
}
}
/**
* Back on the ogl thread: update the terrain patch geometries
* @param updatedPatches to be updated
*/
private void updateQuadLODs() {
synchronized (updatePatchesLock) {
if (updatedPatches == null || updatedPatches.isEmpty())
return;
// do the actual geometry update here
for (UpdatedTerrainPatch utp : updatedPatches.values()) {
utp.updateAll();
}
updatedPatches.clear();
}
}
public boolean hasPatchesToUpdate() {
return updatedPatches != null && !updatedPatches.isEmpty();
}
private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {
boolean theSame = true;
for (Vector3f l : locations) {
for (Vector3f v : lastCameraLocations) {
if (!v.equals(l) ) {
theSame = false;
return false;
}
}
}
return theSame;
}
private synchronized boolean isLodCalcRunning() {
return lodCalcRunning;
}
private synchronized void setLodCalcRunning(boolean running) {
lodCalcRunning = running;
}
private List<Vector3f> cloneVectorList(List<Vector3f> locations) {
List<Vector3f> cloned = new ArrayList<Vector3f>();
for(Vector3f l : locations)
cloned.add(l.clone());
return cloned;
}
public Control cloneForSpatial(Spatial spatial) {
if (spatial instanceof Terrain) {
List<Camera> cameraClone = new ArrayList<Camera>();
@ -190,6 +315,56 @@ public class TerrainLodControl extends AbstractControl {
lodCalculator.turnOnLod();
}
}
/**
* Calculates the LOD of all child terrain patches.
*/
private class UpdateLOD implements Runnable {
private List<Vector3f> camLocations;
private LodCalculator lodCalculator;
UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
this.camLocations = camLocations;
this.lodCalculator = lodCalculator;
}
public void run() {
long start = System.currentTimeMillis();
if (isLodCalcRunning()) {
//System.out.println("thread already running");
return;
}
//System.out.println("spawned thread "+toString());
setLodCalcRunning(true);
TerrainQuad terrainQuad = (TerrainQuad)getSpatial();
// go through each patch and calculate its LOD based on camera distance
HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
boolean lodChanged = terrainQuad.calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
if (!lodChanged) {
// not worth updating anything else since no one's LOD changed
setLodCalcRunning(false);
return;
}
// then calculate its neighbour LOD values for seaming in the shader
terrainQuad.findNeighboursLod(updated);
terrainQuad.fixEdges(updated); // 'updated' can get added to here
terrainQuad.reIndexPages(updated, lodCalculator.usesVariableLod());
setUpdateQuadLODs(updated); // set back to main ogl thread
setLodCalcRunning(false);
//double duration = (System.currentTimeMillis()-start);
//System.out.println("terminated in "+duration);
}
}
@Override
public void write(JmeExporter ex) throws IOException {

@ -106,6 +106,9 @@ public class TerrainPatch extends Geometry {
protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;
protected boolean searchedForNeighboursAlready = false;
// these two vectors are calculated on the GL thread, but used in the outside LOD thread
protected Vector3f worldTranslationCached;
protected Vector3f worldScaleCached;
protected float[] lodEntropy;
@ -942,6 +945,22 @@ public class TerrainPatch extends Geometry {
}
}
/**
* Caches the transforms (except rotation) so the LOD calculator,
* which runs on a separate thread, can access them safely.
*/
protected void cacheTerrainTransforms() {
this.worldScaleCached = getWorldScale().clone();
this.worldTranslationCached = getWorldTranslation().clone();
}
public Vector3f getWorldScaleCached() {
return worldScaleCached;
}
public Vector3f getWorldTranslationCached() {
return worldTranslationCached;
}
}

@ -61,9 +61,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -104,34 +101,12 @@ public class TerrainQuad extends Node implements Terrain {
protected float offsetAmount;
protected int quadrant = 0; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
//protected LodCalculatorFactory lodCalculatorFactory;
//protected LodCalculator lodCalculator;
protected List<Vector3f> lastCameraLocations; // used for LOD calc
private boolean lodCalcRunning = false;
private int lodOffCount = 0;
private int maxLod = -1;
private HashMap<String,UpdatedTerrainPatch> updatedPatches;
private final Object updatePatchesLock = new Object();
private BoundingBox affectedAreaBBox; // only set in the root quad
private TerrainPicker picker;
private Vector3f lastScale = Vector3f.UNIT_XYZ;
protected ExecutorService executor;
protected ExecutorService createExecutorService() {
return Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread th = new Thread(r);
th.setName("jME Terrain Thread");
th.setDaemon(true);
return th;
}
});
}
public TerrainQuad() {
super("Terrain");
}
@ -220,24 +195,9 @@ public class TerrainQuad extends Node implements Terrain {
this.size = quadSize;
this.patchSize = patchSize;
this.stepScale = scale;
//this.lodCalculatorFactory = lodCalculatorFactory;
//this.lodCalculator = lodCalculator;
split(patchSize, heightMap);
}
/*public void setLodCalculatorFactory(LodCalculatorFactory lodCalculatorFactory) {
if (children != null) {
for (int i = children.size(); --i >= 0;) {
Spatial child = children.get(i);
if (child instanceof TerrainQuad) {
((TerrainQuad) child).setLodCalculatorFactory(lodCalculatorFactory);
} else if (child instanceof TerrainPatch) {
((TerrainPatch) child).setLodCalculator(lodCalculatorFactory.createCalculator((TerrainPatch) child));
}
}
}
}*/
/**
* Forces the recalculation of all normals on the terrain.
*/
@ -253,16 +213,6 @@ public class TerrainQuad extends Node implements Terrain {
return heightMap;
}
/**
* Call from the update() method of a terrain controller to update
* the LOD values of each patch.
* This will perform the geometry calculation in a background thread and
* do the actual update on the opengl thread.
*/
public void update(List<Vector3f> locations, LodCalculator lodCalculator) {
updateLOD(locations, lodCalculator);
}
/**
* update the normals if there were any height changes recently.
* Should only be called on the root quad
@ -277,73 +227,20 @@ public class TerrainQuad extends Node implements Terrain {
setNormalRecalcNeeded(null); // set to false
}
}
// do all of the LOD calculations
protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
// update any existing ones that need updating
updateQuadLODs();
if (lodCalculator.isLodOff()) {
// we want to calculate the base lod at least once
if (lodOffCount == 1)
return;
else
lodOffCount++;
} else
lodOffCount = 0;
if (lastCameraLocations != null) {
if (lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())
return; // don't update if in same spot
else
lastCameraLocations = cloneVectorList(locations);
}
else {
lastCameraLocations = cloneVectorList(locations);
return;
}
if (isLodCalcRunning()) {
return;
}
if (getParent() instanceof TerrainQuad) {
return; // we just want the root quad to perform this.
}
if (executor == null)
executor = createExecutorService();
UpdateLOD updateLodThread = new UpdateLOD(locations, lodCalculator);
executor.execute(updateLodThread);
}
private synchronized boolean isLodCalcRunning() {
return lodCalcRunning;
}
private synchronized void setLodCalcRunning(boolean running) {
lodCalcRunning = running;
}
private List<Vector3f> cloneVectorList(List<Vector3f> locations) {
List<Vector3f> cloned = new ArrayList<Vector3f>();
for(Vector3f l : locations)
cloned.add(l.clone());
return cloned;
}
private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {
boolean theSame = true;
for (Vector3f l : locations) {
for (Vector3f v : lastCameraLocations) {
if (!v.equals(l) ) {
theSame = false;
return false;
}
/**
* Caches the transforms (except rotation) so the LOD calculator,
* which runs on a separate thread, can access them safely.
*/
protected void cacheTerrainTransforms() {
for (int i = children.size(); --i >= 0;) {
Spatial child = children.get(i);
if (child instanceof TerrainQuad) {
((TerrainQuad) child).cacheTerrainTransforms();
} else if (child instanceof TerrainPatch) {
((TerrainPatch) child).cacheTerrainTransforms();
}
}
return theSame;
}
private int collideWithRay(Ray ray, CollisionResults results) {
@ -412,86 +309,10 @@ public class TerrainQuad extends Node implements Terrain {
return null;
}
//public float getTextureCoordinateScale() {
// return 1f/(float)totalSize;
//}
public int getNumMajorSubdivisions() {
return 1;
}
/**
* Calculates the LOD of all child terrain patches.
*/
private class UpdateLOD implements Runnable {
private List<Vector3f> camLocations;
private LodCalculator lodCalculator;
UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
this.camLocations = camLocations;
this.lodCalculator = lodCalculator;
}
public void run() {
long start = System.currentTimeMillis();
if (isLodCalcRunning()) {
//System.out.println("thread already running");
return;
}
//System.out.println("spawned thread "+toString());
setLodCalcRunning(true);
// go through each patch and calculate its LOD based on camera distance
HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
boolean lodChanged = calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
if (!lodChanged) {
// not worth updating anything else since no one's LOD changed
setLodCalcRunning(false);
return;
}
// then calculate its neighbour LOD values for seaming in the shader
findNeighboursLod(updated);
fixEdges(updated); // 'updated' can get added to here
reIndexPages(updated, lodCalculator.usesVariableLod());
setUpdateQuadLODs(updated); // set back to main ogl thread
setLodCalcRunning(false);
//double duration = (System.currentTimeMillis()-start);
//System.out.println("terminated in "+duration);
}
}
private void setUpdateQuadLODs(HashMap<String,UpdatedTerrainPatch> updated) {
synchronized (updatePatchesLock) {
updatedPatches = updated;
}
}
/**
* Back on the ogl thread: update the terrain patch geometries
* @param updatedPatches to be updated
*/
private void updateQuadLODs() {
synchronized (updatePatchesLock) {
if (updatedPatches == null || updatedPatches.isEmpty())
return;
// do the actual geometry update here
for (UpdatedTerrainPatch utp : updatedPatches.values()) {
utp.updateAll();
}
updatedPatches.clear();
}
}
public boolean hasPatchesToUpdate() {
return updatedPatches != null && !updatedPatches.isEmpty();
}
protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) {

@ -44,140 +44,140 @@ import java.nio.IntBuffer;
*/
public class UpdatedTerrainPatch {
private TerrainPatch updatedPatch;
private int newLod;
private int previousLod;
private int rightLod,topLod,leftLod,bottomLod;
private IntBuffer newIndexBuffer;
private boolean reIndexNeeded = false;
private boolean fixEdges = false;
private TerrainPatch updatedPatch;
private int newLod;
private int previousLod;
private int rightLod,topLod,leftLod,bottomLod;
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) {
this.updatedPatch = updatedPatch;
this.newLod = newLod;
}
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {
this.updatedPatch = updatedPatch;
this.newLod = newLod;
this.previousLod = prevLOD;
this.reIndexNeeded = reIndexNeeded;
if (this.newLod <= 0)
throw new IllegalArgumentException();
}
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {
this.updatedPatch = updatedPatch;
this.newLod = newLod;
this.previousLod = prevLOD;
this.reIndexNeeded = reIndexNeeded;
if (this.newLod <= 0)
throw new IllegalArgumentException();
}
public String getName() {
return updatedPatch.getName();
}
public String getName() {
return updatedPatch.getName();
}
protected boolean lodChanged() {
if (reIndexNeeded && previousLod != newLod)
return true;
else
return false;
}
protected boolean lodChanged() {
if (reIndexNeeded && previousLod != newLod)
return true;
else
return false;
}
protected TerrainPatch getUpdatedPatch() {
return updatedPatch;
}
protected TerrainPatch getUpdatedPatch() {
return updatedPatch;
}
protected void setUpdatedPatch(TerrainPatch updatedPatch) {
this.updatedPatch = updatedPatch;
}
protected void setUpdatedPatch(TerrainPatch updatedPatch) {
this.updatedPatch = updatedPatch;
}
protected int getNewLod() {
return newLod;
}
protected int getNewLod() {
return newLod;
}
public void setNewLod(int newLod) {
this.newLod = newLod;
if (this.newLod < 0)
throw new IllegalArgumentException();
}
public void setNewLod(int newLod) {
this.newLod = newLod;
if (this.newLod < 0)
throw new IllegalArgumentException();
}
protected IntBuffer getNewIndexBuffer() {
return newIndexBuffer;
}
protected IntBuffer getNewIndexBuffer() {
return newIndexBuffer;
}
protected void setNewIndexBuffer(IntBuffer newIndexBuffer) {
this.newIndexBuffer = newIndexBuffer;
}
protected void setNewIndexBuffer(IntBuffer newIndexBuffer) {
this.newIndexBuffer = newIndexBuffer;
}
protected int getRightLod() {
return rightLod;
}
protected int getRightLod() {
return rightLod;
}
protected void setRightLod(int rightLod) {
this.rightLod = rightLod;
}
protected void setRightLod(int rightLod) {
this.rightLod = rightLod;
}
protected int getTopLod() {
return topLod;
}
protected int getTopLod() {
return topLod;
}
protected void setTopLod(int topLod) {
this.topLod = topLod;
}
protected void setTopLod(int topLod) {
this.topLod = topLod;
}
protected int getLeftLod() {
return leftLod;
}
protected int getLeftLod() {
return leftLod;
}
protected void setLeftLod(int leftLod) {
this.leftLod = leftLod;
}
protected void setLeftLod(int leftLod) {
this.leftLod = leftLod;
}
protected int getBottomLod() {
return bottomLod;
}
protected int getBottomLod() {
return bottomLod;
}
protected void setBottomLod(int bottomLod) {
this.bottomLod = bottomLod;
}
protected void setBottomLod(int bottomLod) {
this.bottomLod = bottomLod;
}
public boolean isReIndexNeeded() {
return reIndexNeeded;
}
public boolean isReIndexNeeded() {
return reIndexNeeded;
}
public void setReIndexNeeded(boolean reIndexNeeded) {
this.reIndexNeeded = reIndexNeeded;
}
public void setReIndexNeeded(boolean reIndexNeeded) {
this.reIndexNeeded = reIndexNeeded;
}
public boolean isFixEdges() {
return fixEdges;
}
public boolean isFixEdges() {
return fixEdges;
}
public void setFixEdges(boolean fixEdges) {
this.fixEdges = fixEdges;
}
public void setFixEdges(boolean fixEdges) {
this.fixEdges = fixEdges;
}
public int getPreviousLod() {
return previousLod;
}
public int getPreviousLod() {
return previousLod;
}
public void setPreviousLod(int previousLod) {
this.previousLod = previousLod;
}
public void setPreviousLod(int previousLod) {
this.previousLod = previousLod;
}
public void updateAll() {
updatedPatch.setLod(newLod);
updatedPatch.setLodRight(rightLod);
updatedPatch.setLodTop(topLod);
updatedPatch.setLodLeft(leftLod);
updatedPatch.setLodBottom(bottomLod);
if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {
updatedPatch.setPreviousLod(previousLod);
updatedPatch.getMesh().clearBuffer(Type.Index);
updatedPatch.getMesh().setBuffer(Type.Index, 3, newIndexBuffer);
}
}
public void updateAll() {
updatedPatch.setLod(newLod);
updatedPatch.setLodRight(rightLod);
updatedPatch.setLodTop(topLod);
updatedPatch.setLodLeft(leftLod);
updatedPatch.setLodBottom(bottomLod);
if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {
updatedPatch.setPreviousLod(previousLod);
updatedPatch.getMesh().clearBuffer(Type.Index);
updatedPatch.getMesh().setBuffer(Type.Index, 3, newIndexBuffer);
}
}
}

@ -82,7 +82,7 @@ public class DistanceLodCalculator implements LodCalculator {
// go through each lod level to find the one we are in
for (int i = 0; i <= terrainPatch.getMaxLod(); i++) {
if (distance < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScale().x || i == terrainPatch.getMaxLod()) {
if (distance < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScaleCached().x || i == terrainPatch.getMaxLod()) {
boolean reIndexNeeded = false;
if (i != terrainPatch.getLod()) {
reIndexNeeded = true;
@ -107,9 +107,9 @@ public class DistanceLodCalculator implements LodCalculator {
}
protected Vector3f getCenterLocation(TerrainPatch terrainPatch) {
Vector3f loc = terrainPatch.getWorldTranslation().clone();
loc.x += terrainPatch.getSize()*terrainPatch.getWorldScale().x / 2;
loc.z += terrainPatch.getSize()*terrainPatch.getWorldScale().z / 2;
Vector3f loc = terrainPatch.getWorldTranslationCached();
loc.x += terrainPatch.getSize()*terrainPatch.getWorldScaleCached().x / 2;
loc.z += terrainPatch.getSize()*terrainPatch.getWorldScaleCached().z / 2;
return loc;
}

@ -7,6 +7,7 @@ import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
@ -128,7 +129,7 @@ public class TerrainFractalGridTest extends SimpleApplication {
this.terrain.setLocalScale(2f, 1f, 2f);
this.rootNode.attachChild(this.terrain);
TerrainLodControl control = new TerrainLodControl(this.terrain, this.getCamera());
TerrainLodControl control = new TerrainGridLodControl(this.terrain, this.getCamera());
control.setLodCalculator(new DistanceLodCalculator(33, 2.7f)); // patch size, and a multiplier
this.terrain.addControl(control);

@ -25,6 +25,7 @@ import com.jme3.scene.Spatial;
import com.jme3.scene.debug.Arrow;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
@ -158,9 +159,7 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
this.terrain.setLocalScale(2f, 1f, 2f);
this.rootNode.attachChild(this.terrain);
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(this.getCamera());
TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
TerrainLodControl control = new TerrainGridLodControl(this.terrain, this.getCamera());
control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control);

@ -17,6 +17,7 @@ import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
@ -50,7 +51,7 @@ public class TerrainGridSerializationTest extends SimpleApplication {
this.rootNode.attachChild(this.terrain);
TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
TerrainLodControl control = new TerrainGridLodControl(this.terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control);

@ -18,6 +18,7 @@ import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.grid.ImageTileLoader;
@ -107,7 +108,7 @@ public class TerrainGridTest extends SimpleApplication {
this.terrain.setLocalScale(1f, 1f, 1f);
this.rootNode.attachChild(this.terrain);
TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
TerrainLodControl control = new TerrainGridLodControl(this.terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control);

@ -17,6 +17,7 @@ import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.terrain.geomipmap.TerrainGrid;
import com.jme3.terrain.geomipmap.TerrainGridListener;
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.grid.AssetTileLoader;
@ -108,7 +109,7 @@ public class TerrainGridTileLoaderTest extends SimpleApplication {
this.rootNode.attachChild(this.terrain);
TerrainLodControl control = new TerrainLodControl(this.terrain, getCamera());
TerrainLodControl control = new TerrainGridLodControl(this.terrain, getCamera());
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
this.terrain.addControl(control);

Loading…
Cancel
Save