* 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
This commit is contained in:
parent
198979bf5a
commit
a1249b551d
@ -125,17 +125,6 @@ public interface Terrain {
|
|||||||
*/
|
*/
|
||||||
public int getMaxLod();
|
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.
|
* Lock or unlock the meshes of this terrain.
|
||||||
* Locked meshes are un-editable but have better performance.
|
* Locked meshes are un-editable but have better performance.
|
||||||
|
@ -51,6 +51,9 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
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.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -111,9 +114,10 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
protected Set<TerrainGridListener> listeners = new HashSet<TerrainGridListener>();
|
protected Set<TerrainGridListener> listeners = new HashSet<TerrainGridListener>();
|
||||||
protected Material material;
|
protected Material material;
|
||||||
protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(16);
|
protected LRUCache<Vector3f, TerrainQuad> cache = new LRUCache<Vector3f, TerrainQuad>(16);
|
||||||
private int cellsLoaded = 0;
|
protected int cellsLoaded = 0;
|
||||||
private int[] gridOffset;
|
protected int[] gridOffset;
|
||||||
private boolean runOnce = false;
|
protected boolean runOnce = false;
|
||||||
|
protected ExecutorService cacheExecutor;
|
||||||
|
|
||||||
protected class UpdateQuadCache implements Runnable {
|
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) {
|
public Vector3f getCamCell(Vector3f location) {
|
||||||
Vector3f tile = getTileCell(location);
|
Vector3f tile = getTileCell(location);
|
||||||
Vector3f offsetHalf = new Vector3f(-0.5f, 0, -0.5f);
|
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
|
* 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
|
// use the same executor as the LODControl
|
||||||
executor = createExecutorService();
|
cacheExecutor = createExecutorService();
|
||||||
}
|
}
|
||||||
|
|
||||||
executor.submit(new UpdateQuadCache(camCell));
|
cacheExecutor.submit(new UpdateQuadCache(camCell));
|
||||||
|
|
||||||
this.currentCamCell = camCell;
|
this.currentCamCell = camCell;
|
||||||
}
|
}
|
||||||
@ -480,6 +439,17 @@ public class TerrainGrid extends TerrainQuad {
|
|||||||
return terrain.getMaterial(worldLocation);
|
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
|
@Override
|
||||||
public void read(JmeImporter im) throws IOException {
|
public void read(JmeImporter im) throws IOException {
|
||||||
super.read(im);
|
super.read(im);
|
||||||
|
@ -48,7 +48,11 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
|
|||||||
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
|
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
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.
|
* Tells the terrain to update its Level of Detail.
|
||||||
@ -73,6 +77,15 @@ public class TerrainLodControl extends AbstractControl {
|
|||||||
private LodCalculator lodCalculator;
|
private LodCalculator lodCalculator;
|
||||||
private boolean hasResetLod = false; // used when enabled is set to false
|
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() {
|
public TerrainLodControl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,9 +112,15 @@ public class TerrainLodControl extends AbstractControl {
|
|||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected ExecutorService createExecutorService() {
|
||||||
public void update(float tpf) {
|
return Executors.newSingleThreadExecutor(new ThreadFactory() {
|
||||||
controlUpdate(tpf);
|
public Thread newThread(Runnable r) {
|
||||||
|
Thread th = new Thread(r);
|
||||||
|
th.setName("jME Terrain Thread");
|
||||||
|
th.setDaemon(true);
|
||||||
|
return th;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -126,10 +145,116 @@ public class TerrainLodControl extends AbstractControl {
|
|||||||
cameraLocations.add(c.getLocation());
|
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) {
|
public Control cloneForSpatial(Spatial spatial) {
|
||||||
if (spatial instanceof Terrain) {
|
if (spatial instanceof Terrain) {
|
||||||
List<Camera> cameraClone = new ArrayList<Camera>();
|
List<Camera> cameraClone = new ArrayList<Camera>();
|
||||||
@ -191,6 +316,56 @@ public class TerrainLodControl extends AbstractControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public void write(JmeExporter ex) throws IOException {
|
public void write(JmeExporter ex) throws IOException {
|
||||||
super.write(ex);
|
super.write(ex);
|
||||||
|
@ -106,6 +106,9 @@ public class TerrainPatch extends Geometry {
|
|||||||
protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;
|
protected TerrainPatch leftNeighbour, topNeighbour, rightNeighbour, bottomNeighbour;
|
||||||
protected boolean searchedForNeighboursAlready = false;
|
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;
|
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.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -104,34 +101,12 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
protected float offsetAmount;
|
protected float offsetAmount;
|
||||||
|
|
||||||
protected int quadrant = 0; // 1=upper left, 2=lower left, 3=upper right, 4=lower right
|
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 int maxLod = -1;
|
||||||
private HashMap<String,UpdatedTerrainPatch> updatedPatches;
|
|
||||||
private final Object updatePatchesLock = new Object();
|
|
||||||
private BoundingBox affectedAreaBBox; // only set in the root quad
|
private BoundingBox affectedAreaBBox; // only set in the root quad
|
||||||
|
|
||||||
private TerrainPicker picker;
|
private TerrainPicker picker;
|
||||||
private Vector3f lastScale = Vector3f.UNIT_XYZ;
|
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() {
|
public TerrainQuad() {
|
||||||
super("Terrain");
|
super("Terrain");
|
||||||
}
|
}
|
||||||
@ -220,24 +195,9 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
this.size = quadSize;
|
this.size = quadSize;
|
||||||
this.patchSize = patchSize;
|
this.patchSize = patchSize;
|
||||||
this.stepScale = scale;
|
this.stepScale = scale;
|
||||||
//this.lodCalculatorFactory = lodCalculatorFactory;
|
|
||||||
//this.lodCalculator = lodCalculator;
|
|
||||||
split(patchSize, heightMap);
|
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.
|
* Forces the recalculation of all normals on the terrain.
|
||||||
*/
|
*/
|
||||||
@ -253,16 +213,6 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
return heightMap;
|
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.
|
* update the normals if there were any height changes recently.
|
||||||
* Should only be called on the root quad
|
* Should only be called on the root quad
|
||||||
@ -278,72 +228,19 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do all of the LOD calculations
|
/**
|
||||||
protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
|
* Caches the transforms (except rotation) so the LOD calculator,
|
||||||
// update any existing ones that need updating
|
* which runs on a separate thread, can access them safely.
|
||||||
updateQuadLODs();
|
*/
|
||||||
|
protected void cacheTerrainTransforms() {
|
||||||
if (lodCalculator.isLodOff()) {
|
for (int i = children.size(); --i >= 0;) {
|
||||||
// we want to calculate the base lod at least once
|
Spatial child = children.get(i);
|
||||||
if (lodOffCount == 1)
|
if (child instanceof TerrainQuad) {
|
||||||
return;
|
((TerrainQuad) child).cacheTerrainTransforms();
|
||||||
else
|
} else if (child instanceof TerrainPatch) {
|
||||||
lodOffCount++;
|
((TerrainPatch) child).cacheTerrainTransforms();
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return theSame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int collideWithRay(Ray ray, CollisionResults results) {
|
private int collideWithRay(Ray ray, CollisionResults results) {
|
||||||
@ -412,86 +309,10 @@ public class TerrainQuad extends Node implements Terrain {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//public float getTextureCoordinateScale() {
|
|
||||||
// return 1f/(float)totalSize;
|
|
||||||
//}
|
|
||||||
public int getNumMajorSubdivisions() {
|
public int getNumMajorSubdivisions() {
|
||||||
return 1;
|
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) {
|
protected boolean calculateLod(List<Vector3f> location, HashMap<String,UpdatedTerrainPatch> updates, LodCalculator lodCalculator) {
|
||||||
|
|
||||||
|
@ -44,140 +44,140 @@ import java.nio.IntBuffer;
|
|||||||
*/
|
*/
|
||||||
public class UpdatedTerrainPatch {
|
public class UpdatedTerrainPatch {
|
||||||
|
|
||||||
private TerrainPatch updatedPatch;
|
private TerrainPatch updatedPatch;
|
||||||
private int newLod;
|
private int newLod;
|
||||||
private int previousLod;
|
private int previousLod;
|
||||||
private int rightLod,topLod,leftLod,bottomLod;
|
private int rightLod,topLod,leftLod,bottomLod;
|
||||||
private IntBuffer newIndexBuffer;
|
private IntBuffer newIndexBuffer;
|
||||||
private boolean reIndexNeeded = false;
|
private boolean reIndexNeeded = false;
|
||||||
private boolean fixEdges = false;
|
private boolean fixEdges = false;
|
||||||
|
|
||||||
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {
|
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {
|
||||||
this.updatedPatch = updatedPatch;
|
this.updatedPatch = updatedPatch;
|
||||||
this.newLod = newLod;
|
this.newLod = newLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {
|
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {
|
||||||
this.updatedPatch = updatedPatch;
|
this.updatedPatch = updatedPatch;
|
||||||
this.newLod = newLod;
|
this.newLod = newLod;
|
||||||
this.previousLod = prevLOD;
|
this.previousLod = prevLOD;
|
||||||
this.reIndexNeeded = reIndexNeeded;
|
this.reIndexNeeded = reIndexNeeded;
|
||||||
if (this.newLod <= 0)
|
if (this.newLod <= 0)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return updatedPatch.getName();
|
return updatedPatch.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean lodChanged() {
|
protected boolean lodChanged() {
|
||||||
if (reIndexNeeded && previousLod != newLod)
|
if (reIndexNeeded && previousLod != newLod)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TerrainPatch getUpdatedPatch() {
|
protected TerrainPatch getUpdatedPatch() {
|
||||||
return updatedPatch;
|
return updatedPatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setUpdatedPatch(TerrainPatch updatedPatch) {
|
protected void setUpdatedPatch(TerrainPatch updatedPatch) {
|
||||||
this.updatedPatch = updatedPatch;
|
this.updatedPatch = updatedPatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getNewLod() {
|
protected int getNewLod() {
|
||||||
return newLod;
|
return newLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNewLod(int newLod) {
|
public void setNewLod(int newLod) {
|
||||||
this.newLod = newLod;
|
this.newLod = newLod;
|
||||||
if (this.newLod < 0)
|
if (this.newLod < 0)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IntBuffer getNewIndexBuffer() {
|
protected IntBuffer getNewIndexBuffer() {
|
||||||
return newIndexBuffer;
|
return newIndexBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setNewIndexBuffer(IntBuffer newIndexBuffer) {
|
protected void setNewIndexBuffer(IntBuffer newIndexBuffer) {
|
||||||
this.newIndexBuffer = newIndexBuffer;
|
this.newIndexBuffer = newIndexBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected int getRightLod() {
|
protected int getRightLod() {
|
||||||
return rightLod;
|
return rightLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void setRightLod(int rightLod) {
|
protected void setRightLod(int rightLod) {
|
||||||
this.rightLod = rightLod;
|
this.rightLod = rightLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected int getTopLod() {
|
protected int getTopLod() {
|
||||||
return topLod;
|
return topLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void setTopLod(int topLod) {
|
protected void setTopLod(int topLod) {
|
||||||
this.topLod = topLod;
|
this.topLod = topLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected int getLeftLod() {
|
protected int getLeftLod() {
|
||||||
return leftLod;
|
return leftLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void setLeftLod(int leftLod) {
|
protected void setLeftLod(int leftLod) {
|
||||||
this.leftLod = leftLod;
|
this.leftLod = leftLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected int getBottomLod() {
|
protected int getBottomLod() {
|
||||||
return bottomLod;
|
return bottomLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void setBottomLod(int bottomLod) {
|
protected void setBottomLod(int bottomLod) {
|
||||||
this.bottomLod = bottomLod;
|
this.bottomLod = bottomLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReIndexNeeded() {
|
public boolean isReIndexNeeded() {
|
||||||
return reIndexNeeded;
|
return reIndexNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReIndexNeeded(boolean reIndexNeeded) {
|
public void setReIndexNeeded(boolean reIndexNeeded) {
|
||||||
this.reIndexNeeded = reIndexNeeded;
|
this.reIndexNeeded = reIndexNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFixEdges() {
|
public boolean isFixEdges() {
|
||||||
return fixEdges;
|
return fixEdges;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFixEdges(boolean fixEdges) {
|
public void setFixEdges(boolean fixEdges) {
|
||||||
this.fixEdges = fixEdges;
|
this.fixEdges = fixEdges;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPreviousLod() {
|
public int getPreviousLod() {
|
||||||
return previousLod;
|
return previousLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPreviousLod(int previousLod) {
|
public void setPreviousLod(int previousLod) {
|
||||||
this.previousLod = previousLod;
|
this.previousLod = previousLod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateAll() {
|
public void updateAll() {
|
||||||
updatedPatch.setLod(newLod);
|
updatedPatch.setLod(newLod);
|
||||||
updatedPatch.setLodRight(rightLod);
|
updatedPatch.setLodRight(rightLod);
|
||||||
updatedPatch.setLodTop(topLod);
|
updatedPatch.setLodTop(topLod);
|
||||||
updatedPatch.setLodLeft(leftLod);
|
updatedPatch.setLodLeft(leftLod);
|
||||||
updatedPatch.setLodBottom(bottomLod);
|
updatedPatch.setLodBottom(bottomLod);
|
||||||
if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {
|
if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {
|
||||||
updatedPatch.setPreviousLod(previousLod);
|
updatedPatch.setPreviousLod(previousLod);
|
||||||
updatedPatch.getMesh().clearBuffer(Type.Index);
|
updatedPatch.getMesh().clearBuffer(Type.Index);
|
||||||
updatedPatch.getMesh().setBuffer(Type.Index, 3, newIndexBuffer);
|
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
|
// 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 < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScale().x || i == terrainPatch.getMaxLod()) {
|
if (distance < getLodDistanceThreshold() * (i + 1)*terrainPatch.getWorldScaleCached().x || i == terrainPatch.getMaxLod()) {
|
||||||
boolean reIndexNeeded = false;
|
boolean reIndexNeeded = false;
|
||||||
if (i != terrainPatch.getLod()) {
|
if (i != terrainPatch.getLod()) {
|
||||||
reIndexNeeded = true;
|
reIndexNeeded = true;
|
||||||
@ -107,9 +107,9 @@ public class DistanceLodCalculator implements LodCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Vector3f getCenterLocation(TerrainPatch terrainPatch) {
|
protected Vector3f getCenterLocation(TerrainPatch terrainPatch) {
|
||||||
Vector3f loc = terrainPatch.getWorldTranslation().clone();
|
Vector3f loc = terrainPatch.getWorldTranslationCached();
|
||||||
loc.x += terrainPatch.getSize()*terrainPatch.getWorldScale().x / 2;
|
loc.x += terrainPatch.getSize()*terrainPatch.getWorldScaleCached().x / 2;
|
||||||
loc.z += terrainPatch.getSize()*terrainPatch.getWorldScale().z / 2;
|
loc.z += terrainPatch.getSize()*terrainPatch.getWorldScaleCached().z / 2;
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import com.jme3.material.Material;
|
|||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.terrain.geomipmap.TerrainGrid;
|
import com.jme3.terrain.geomipmap.TerrainGrid;
|
||||||
|
import com.jme3.terrain.geomipmap.TerrainGridLodControl;
|
||||||
import com.jme3.terrain.geomipmap.TerrainLodControl;
|
import com.jme3.terrain.geomipmap.TerrainLodControl;
|
||||||
import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
|
import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
|
||||||
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
|
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
|
||||||
@ -128,7 +129,7 @@ public class TerrainFractalGridTest extends SimpleApplication {
|
|||||||
this.terrain.setLocalScale(2f, 1f, 2f);
|
this.terrain.setLocalScale(2f, 1f, 2f);
|
||||||
this.rootNode.attachChild(this.terrain);
|
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
|
control.setLodCalculator(new DistanceLodCalculator(33, 2.7f)); // patch size, and a multiplier
|
||||||
this.terrain.addControl(control);
|
this.terrain.addControl(control);
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import com.jme3.scene.Spatial;
|
|||||||
import com.jme3.scene.debug.Arrow;
|
import com.jme3.scene.debug.Arrow;
|
||||||
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.TerrainGridLodControl;
|
||||||
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.grid.FractalTileLoader;
|
import com.jme3.terrain.geomipmap.grid.FractalTileLoader;
|
||||||
@ -158,9 +159,7 @@ public class TerrainGridAlphaMapTest extends SimpleApplication {
|
|||||||
this.terrain.setLocalScale(2f, 1f, 2f);
|
this.terrain.setLocalScale(2f, 1f, 2f);
|
||||||
this.rootNode.attachChild(this.terrain);
|
this.rootNode.attachChild(this.terrain);
|
||||||
|
|
||||||
List<Camera> cameras = new ArrayList<Camera>();
|
TerrainLodControl control = new TerrainGridLodControl(this.terrain, this.getCamera());
|
||||||
cameras.add(this.getCamera());
|
|
||||||
TerrainLodControl control = new TerrainLodControl(this.terrain, cameras);
|
|
||||||
control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
|
control.setLodCalculator( new DistanceLodCalculator(33, 2.7f) ); // patch size, and a multiplier
|
||||||
this.terrain.addControl(control);
|
this.terrain.addControl(control);
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import com.jme3.math.ColorRGBA;
|
|||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
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.TerrainGridLodControl;
|
||||||
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.geomipmap.lodcalc.DistanceLodCalculator;
|
||||||
@ -50,7 +51,7 @@ public class TerrainGridSerializationTest extends SimpleApplication {
|
|||||||
|
|
||||||
this.rootNode.attachChild(this.terrain);
|
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
|
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
|
||||||
this.terrain.addControl(control);
|
this.terrain.addControl(control);
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import com.jme3.math.ColorRGBA;
|
|||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
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.TerrainGridLodControl;
|
||||||
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.grid.ImageTileLoader;
|
import com.jme3.terrain.geomipmap.grid.ImageTileLoader;
|
||||||
@ -107,7 +108,7 @@ public class TerrainGridTest extends SimpleApplication {
|
|||||||
this.terrain.setLocalScale(1f, 1f, 1f);
|
this.terrain.setLocalScale(1f, 1f, 1f);
|
||||||
this.rootNode.attachChild(this.terrain);
|
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
|
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
|
||||||
this.terrain.addControl(control);
|
this.terrain.addControl(control);
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import com.jme3.math.ColorRGBA;
|
|||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
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.TerrainGridLodControl;
|
||||||
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.grid.AssetTileLoader;
|
import com.jme3.terrain.geomipmap.grid.AssetTileLoader;
|
||||||
@ -108,7 +109,7 @@ public class TerrainGridTileLoaderTest extends SimpleApplication {
|
|||||||
|
|
||||||
this.rootNode.attachChild(this.terrain);
|
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
|
control.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
|
||||||
this.terrain.addControl(control);
|
this.terrain.addControl(control);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user