Softened terrain API to allow for different tiling implementations. Added MultiTerrainLodControl

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9434 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
bre..ns 13 years ago
parent b5aa49bc16
commit 33a69d4536
  1. 11
      engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
  2. 127
      engine/src/terrain/com/jme3/terrain/geomipmap/MultiTerrainLodControl.java
  3. 14
      engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java
  4. 100
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java
  5. 4
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
  6. 164
      engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
  7. 39
      engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java
  8. 12
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java
  9. 8
      engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java
  10. 73
      engine/src/test/jme3test/terrain/TerrainTestTile.java

@ -154,6 +154,9 @@ public class LODGeomap extends GeoMap {
*/
public IntBuffer writeIndexArrayLodDiff(IntBuffer store, int lod, boolean rightLod, boolean topLod, boolean leftLod, boolean bottomLod) {
//if (true)
//return writeIndexArrayLodVariable(store, lod, height, lod, lod, lod);
IntBuffer buffer2 = store;
int numIndexes = calculateNumIndexesLodDiff(lod);
if (store == null) {
@ -275,7 +278,7 @@ public class LODGeomap extends GeoMap {
buffer.put(idx);
idx = (row + 2 * lod) * getWidth();
buffer.put(idx);
if (row < getWidth() - lod - 2 - 1) { //if not the last one
if (row < getWidth() - 1 - 2 * lod) { //if not the last one
idx = (row + 2 * lod) * getWidth() + lod;
buffer.put(idx);
idx = (row + 2 * lod) * getWidth();
@ -917,10 +920,12 @@ public class LODGeomap extends GeoMap {
public void put(int value) {
try {
delegate.put(value);
count++;
if (count > delegate.limit())
throw new BufferOverflowException();
delegate.put(value);
} catch (BufferOverflowException e) {
//System.out.println("err buffer size: "+delegate.capacity());
System.out.println("err buffer size: "+delegate.capacity());
}
}

@ -0,0 +1,127 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.terrain.geomipmap;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* An extension of the TerrainLodControl that handles
* multiple terrains at once. This is to be used if you
* have your own tiling/paging terrain system, such as
* TerrainGrid.
*
* @author Brent Owens
*/
public class MultiTerrainLodControl extends TerrainLodControl {
List<TerrainQuad> terrains = new ArrayList<TerrainQuad>();
private List<TerrainQuad> addedTerrains = new ArrayList<TerrainQuad>();
private List<TerrainQuad> removedTerrains = new ArrayList<TerrainQuad>();
public MultiTerrainLodControl(List<Camera> cameras) {
this.cameras = cameras;
lodCalculator = new DistanceLodCalculator(65, 2.7f);
}
public MultiTerrainLodControl(Camera camera) {
List<Camera> cams = new ArrayList<Camera>();
cams.add(camera);
this.cameras = cams;
lodCalculator = new DistanceLodCalculator(65, 2.7f);
}
/**
* Add a terrain that will have its LOD handled by this control.
* It will be added next update run. You should only call this from
* the render thread.
*/
public void addTerrain(TerrainQuad tq) {
addedTerrains.add(tq);
}
/**
* Add a terrain that will no longer have its LOD handled by this control.
* It will be removed next update run. You should only call this from
* the render thread.
*/
public void removeTerrain(TerrainQuad tq) {
removedTerrains.remove(tq);
}
@Override
protected UpdateLOD getLodThread(List<Vector3f> locations, LodCalculator lodCalculator) {
return new UpdateMultiLOD(locations, lodCalculator);
}
@Override
protected void prepareTerrain() {
if (!addedTerrains.isEmpty()) {
for (TerrainQuad t : addedTerrains) {
if (!terrains.contains(t))
terrains.add(t);
}
addedTerrains.clear();
}
if (!removedTerrains.isEmpty()) {
terrains.removeAll(removedTerrains);
removedTerrains.clear();
}
for (TerrainQuad terrain : terrains)
terrain.cacheTerrainTransforms();// cache the terrain's world transforms so they can be accessed on the separate thread safely
}
/**
* Overrides the parent UpdateLOD runnable to process
* multiple terrains.
*/
protected class UpdateMultiLOD extends UpdateLOD {
protected UpdateMultiLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
super(camLocations, lodCalculator);
}
@Override
public HashMap<String, UpdatedTerrainPatch> call() throws Exception {
setLodCalcRunning(true);
HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
for (TerrainQuad terrainQuad : terrains) {
// go through each patch and calculate its LOD based on camera distance
terrainQuad.calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
}
for (TerrainQuad terrainQuad : terrains) {
// then calculate the neighbour LOD values for seaming
terrainQuad.findNeighboursLod(updated);
}
for (TerrainQuad terrainQuad : terrains) {
// check neighbour quads that need their edges seamed
terrainQuad.fixEdges(updated);
}
for (TerrainQuad terrainQuad : terrains) {
// perform the edge seaming, if it requires it
terrainQuad.reIndexPages(updated, lodCalculator.usesVariableLod());
}
//setUpdateQuadLODs(updated); // set back to main ogl thread
setLodCalcRunning(false);
return updated;
}
}
}

@ -13,7 +13,7 @@ package com.jme3.terrain.geomipmap;
* With this you can have a parent, control or spatial, that manages a group of
* TerrainQuads by linking them together through these four methods.
*
* The general orientation of TerrainQuads and their sun-quads is as such:
* The general orientation of TerrainQuads and their sub-quads is as such:
*
*
* +-- x+ ---->
@ -32,11 +32,23 @@ package com.jme3.terrain.geomipmap;
*/
public interface NeighbourFinder {
/**
* Get the TerrainQuad to the right of the supplied 'center' quad.
*/
public TerrainQuad getRightQuad(TerrainQuad center);
/**
* Get the TerrainQuad to the left of the supplied 'center' quad.
*/
public TerrainQuad getLeftQuad(TerrainQuad center);
/**
* Get the TerrainQuad above the supplied 'center' quad.
*/
public TerrainQuad getTopQuad(TerrainQuad center);
/**
* Get the TerrainQuad below the supplied 'center' quad.
*/
public TerrainQuad getDownQuad(TerrainQuad center);
}

@ -50,9 +50,15 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Tells the terrain to update its Level of Detail.
@ -72,19 +78,20 @@ import java.util.concurrent.ThreadFactory;
public class TerrainLodControl extends AbstractControl {
private Terrain terrain;
private List<Camera> cameras;
protected List<Camera> cameras;
private List<Vector3f> cameraLocations = new ArrayList<Vector3f>();
private LodCalculator lodCalculator;
protected 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 AtomicBoolean lodCalcRunning = new AtomicBoolean(false);
private int lodOffCount = 0;
protected ExecutorService executor;
protected Future<HashMap<String, UpdatedTerrainPatch>> indexer;
public TerrainLodControl() {
}
@ -177,6 +184,7 @@ public class TerrainLodControl extends AbstractControl {
if (isLodCalcRunning()) {
return;
}
setLodCalcRunning(true);
//if (getParent() instanceof TerrainQuad) {
// return; // we just want the root quad to perform this.
@ -185,25 +193,47 @@ public class TerrainLodControl extends AbstractControl {
if (executor == null)
executor = createExecutorService();
prepareTerrain();
UpdateLOD updateLodThread = getLodThread(locations, lodCalculator);
indexer = executor.submit(updateLodThread);
}
protected void prepareTerrain() {
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;
}
protected UpdateLOD getLodThread(List<Vector3f> locations, LodCalculator lodCalculator) {
return new UpdateLOD(locations, lodCalculator);
}
/**
* Back on the ogl thread: update the terrain patch geometries
* @param updatedPatches to be updated
*/
private void updateQuadLODs() {
synchronized (updatePatchesLock) {
if (indexer != null) {
if (indexer.isDone()) {
try {
HashMap<String, UpdatedTerrainPatch> updated = indexer.get();
if (updated != null) {
// do the actual geometry update here
for (UpdatedTerrainPatch utp : updated.values()) {
utp.updateAll();
}
}
} catch (InterruptedException ex) {
Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex);
} finally {
indexer = null;
}
}
}
/*synchronized (updatePatchesLock) {
if (updatedPatches == null || updatedPatches.isEmpty())
return;
@ -213,13 +243,13 @@ public class TerrainLodControl extends AbstractControl {
utp.updateAll();
}
updatedPatches.clear();
}
updatedPatches = null;
}*/
}
public boolean hasPatchesToUpdate() {
return updatedPatches != null && !updatedPatches.isEmpty();
}
//public boolean hasPatchesToUpdate() {
// return updatedPatches != null && !updatedPatches.isEmpty();
//}
private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {
boolean theSame = true;
@ -234,12 +264,12 @@ public class TerrainLodControl extends AbstractControl {
return theSame;
}
private synchronized boolean isLodCalcRunning() {
return lodCalcRunning;
protected synchronized boolean isLodCalcRunning() {
return lodCalcRunning.get();
}
private synchronized void setLodCalcRunning(boolean running) {
lodCalcRunning = running;
protected synchronized void setLodCalcRunning(boolean running) {
lodCalcRunning.set(running);
}
private List<Vector3f> cloneVectorList(List<Vector3f> locations) {
@ -320,22 +350,20 @@ 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;
protected class UpdateLOD implements Callable<HashMap<String,UpdatedTerrainPatch>> {
protected List<Vector3f> camLocations;
protected LodCalculator lodCalculator;
UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
protected 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());
public HashMap<String, UpdatedTerrainPatch> call() throws Exception {
//long start = System.currentTimeMillis();
//if (isLodCalcRunning()) {
// return null;
//}
setLodCalcRunning(true);
TerrainQuad terrainQuad = (TerrainQuad)getSpatial();
@ -347,7 +375,7 @@ public class TerrainLodControl extends AbstractControl {
if (!lodChanged) {
// not worth updating anything else since no one's LOD changed
setLodCalcRunning(false);
return;
return null;
}
@ -358,11 +386,11 @@ public class TerrainLodControl extends AbstractControl {
terrainQuad.reIndexPages(updated, lodCalculator.usesVariableLod());
setUpdateQuadLODs(updated); // set back to main ogl thread
//setUpdateQuadLODs(updated); // set back to main ogl thread
setLodCalcRunning(false);
//double duration = (System.currentTimeMillis()-start);
//System.out.println("terminated in "+duration);
return updated;
}
}

@ -80,7 +80,7 @@ import java.util.List;
public class TerrainPatch extends Geometry {
protected LODGeomap geomap;
protected int lod = -1; // this terrain patch's LOD
protected int lod = 0; // this terrain patch's LOD
private int maxLod = -1;
protected int previousLod = -1;
protected int lodLeft, lodTop, lodRight, lodBottom; // it's neighbour's LODs
@ -234,7 +234,7 @@ public class TerrainPatch extends Geometry {
UpdatedTerrainPatch utp = updated.get(getName());
if (utp != null && (utp.isReIndexNeeded() || utp.isFixEdges()) ) {
if (utp != null && utp.isReIndexNeeded() ) {
int pow = (int) Math.pow(2, utp.getNewLod());
boolean left = utp.getLeftLod() > utp.getNewLod();
boolean top = utp.getTopLod() > utp.getNewLod();

@ -201,6 +201,7 @@ public class TerrainQuad extends Node implements Terrain {
public void setNeighbourFinder(NeighbourFinder neighbourFinder) {
this.neighbourFinder = neighbourFinder;
resetCachedNeighbours();
}
/**
@ -360,6 +361,8 @@ public class TerrainQuad extends Node implements Terrain {
}
TerrainPatch right = patch.rightNeighbour;
TerrainPatch down = patch.bottomNeighbour;
TerrainPatch left = patch.leftNeighbour;
TerrainPatch top = patch.topNeighbour;
UpdatedTerrainPatch utp = updated.get(patch.getName());
if (utp == null) {
@ -370,34 +373,55 @@ public class TerrainQuad extends Node implements Terrain {
if (right != null) {
UpdatedTerrainPatch utpR = updated.get(right.getName());
if (utpR == null) {
utpR = new UpdatedTerrainPatch(right, right.lod);
utpR = new UpdatedTerrainPatch(right);
updated.put(utpR.getName(), utpR);
utpR.setNewLod(right.lod);
}
utp.setRightLod(utpR.getNewLod());
utpR.setLeftLod(utp.getNewLod());
}
if (down != null) {
UpdatedTerrainPatch utpD = updated.get(down.getName());
if (utpD == null) {
utpD = new UpdatedTerrainPatch(down, down.lod);
utpD = new UpdatedTerrainPatch(down);
updated.put(utpD.getName(), utpD);
utpD.setNewLod(down.lod);
}
utp.setBottomLod(utpD.getNewLod());
utpD.setTopLod(utp.getNewLod());
}
if (left != null) {
UpdatedTerrainPatch utpL = updated.get(left.getName());
if (utpL == null) {
utpL = new UpdatedTerrainPatch(left);
updated.put(utpL.getName(), utpL);
utpL.setNewLod(left.lod);
}
utp.setLeftLod(utpL.getNewLod());
utpL.setRightLod(utp.getNewLod());
}
if (top != null) {
UpdatedTerrainPatch utpT = updated.get(top.getName());
if (utpT == null) {
utpT = new UpdatedTerrainPatch(top);
updated.put(utpT.getName(), utpT);
utpT.setNewLod(top.lod);
}
utp.setTopLod(utpT.getNewLod());
utpT.setBottomLod(utp.getNewLod());
}
}
}
}
}
/**
* Reset the cached references of neighbours.
* TerrainQuad caches neighbours for faster LOD checks.
* Sometimes you might want to reset this cache (for instance in TerrainGrid)
*/
protected void resetCachedNeighbours() {
public void resetCachedNeighbours() {
if (children != null) {
for (int x = children.size(); --x >= 0;) {
Spatial child = children.get(x);
@ -441,33 +465,41 @@ public class TerrainQuad extends Node implements Terrain {
if (right != null) {
UpdatedTerrainPatch utpR = updated.get(right.getName());
if (utpR == null) {
utpR = new UpdatedTerrainPatch(right, right.lod);
utpR = new UpdatedTerrainPatch(right);
updated.put(utpR.getName(), utpR);
utpR.setNewLod(right.lod);
}
utpR.setLeftLod(utp.getNewLod());
utpR.setFixEdges(true);
}
if (down != null) {
UpdatedTerrainPatch utpD = updated.get(down.getName());
if (utpD == null) {
utpD = new UpdatedTerrainPatch(down, down.lod);
utpD = new UpdatedTerrainPatch(down);
updated.put(utpD.getName(), utpD);
utpD.setNewLod(down.lod);
}
utpD.setTopLod(utp.getNewLod());
utpD.setFixEdges(true);
}
if (top != null){
UpdatedTerrainPatch utpT = updated.get(top.getName());
if (utpT == null) {
utpT = new UpdatedTerrainPatch(top, top.lod);
utpT = new UpdatedTerrainPatch(top);
updated.put(utpT.getName(), utpT);
utpT.setNewLod(top.lod);
}
utpT.setBottomLod(utp.getNewLod());
utpT.setFixEdges(true);
}
if (left != null){
UpdatedTerrainPatch utpL = updated.get(left.getName());
if (utpL == null) {
utpL = new UpdatedTerrainPatch(left, left.lod);
utpL = new UpdatedTerrainPatch(left);
updated.put(utpL.getName(), utpL);
utpL.setNewLod(left.lod);
}
utpL.setRightLod(utp.getNewLod());
utpL.setFixEdges(true);
}
}
@ -1278,6 +1310,8 @@ public class TerrainQuad extends Node implements Terrain {
}
protected TerrainQuad getQuad(int quad) {
if (quad == 0)
return this;
if (children != null)
for (int x = children.size(); --x >= 0;) {
Spatial child = children.get(x);
@ -1355,7 +1389,7 @@ public class TerrainQuad extends Node implements Terrain {
else if (tp.getQuadrant() == 4)
return getPatch(2);
else if (tp.getQuadrant() == 1) {
// find the patch above and ask it for child 2.
// find the patch above and ask it for child 3.
TerrainQuad quad = findLeftQuad();
if (quad != null)
return quad.getPatch(3);
@ -1370,34 +1404,35 @@ public class TerrainQuad extends Node implements Terrain {
protected TerrainQuad findRightQuad() {
boolean useFinder = false;
if (getParent() == null || !(getParent() instanceof TerrainQuad))
if (neighbourFinder == null)
if (getParent() == null || !(getParent() instanceof TerrainQuad)) {
if (neighbourFinder == null)
return null;
else
useFinder = true;
}
TerrainQuad pQuad = (TerrainQuad) getParent();
TerrainQuad pQuad = null;
if (!useFinder)
pQuad = (TerrainQuad) getParent();
if (quadrant == 1)
return pQuad.getQuad(3);
else if (quadrant == 2)
return pQuad.getQuad(4);
else if (quadrant == 3) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getRightQuad(this);
else
quad = pQuad.findRightQuad();
TerrainQuad quad = pQuad.findRightQuad();
if (quad != null)
return quad.getQuad(1);
} else if (quadrant == 4) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getRightQuad(this);
else
quad = pQuad.findRightQuad();
TerrainQuad quad = pQuad.findRightQuad();
if (quad != null)
return quad.getQuad(2);
} else if (quadrant == 0) {
// at the top quad
if (useFinder) {
TerrainQuad quad = neighbourFinder.getRightQuad(this);
return quad;
}
}
return null;
@ -1405,34 +1440,35 @@ public class TerrainQuad extends Node implements Terrain {
protected TerrainQuad findDownQuad() {
boolean useFinder = false;
if (getParent() == null || !(getParent() instanceof TerrainQuad))
if (neighbourFinder == null)
if (getParent() == null || !(getParent() instanceof TerrainQuad)) {
if (neighbourFinder == null)
return null;
else
useFinder = true;
}
TerrainQuad pQuad = (TerrainQuad) getParent();
TerrainQuad pQuad = null;
if (!useFinder)
pQuad = (TerrainQuad) getParent();
if (quadrant == 1)
return pQuad.getQuad(2);
else if (quadrant == 3)
return pQuad.getQuad(4);
else if (quadrant == 2) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getDownQuad(this);
else
quad = pQuad.findDownQuad();
TerrainQuad quad = pQuad.findDownQuad();
if (quad != null)
return quad.getQuad(1);
} else if (quadrant == 4) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getDownQuad(this);
else
quad = pQuad.findDownQuad();
TerrainQuad quad = pQuad.findDownQuad();
if (quad != null)
return quad.getQuad(3);
} else if (quadrant == 0) {
// at the top quad
if (useFinder) {
TerrainQuad quad = neighbourFinder.getDownQuad(this);
return quad;
}
}
return null;
@ -1440,34 +1476,35 @@ public class TerrainQuad extends Node implements Terrain {
protected TerrainQuad findTopQuad() {
boolean useFinder = false;
if (getParent() == null || !(getParent() instanceof TerrainQuad))
if (neighbourFinder == null)
if (getParent() == null || !(getParent() instanceof TerrainQuad)) {
if (neighbourFinder == null)
return null;
else
useFinder = true;
}
TerrainQuad pQuad = (TerrainQuad) getParent();
TerrainQuad pQuad = null;
if (!useFinder)
pQuad = (TerrainQuad) getParent();
if (quadrant == 2)
return pQuad.getQuad(1);
else if (quadrant == 4)
return pQuad.getQuad(3);
else if (quadrant == 1) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getTopQuad(this);
else
quad = pQuad.findTopQuad();
TerrainQuad quad = pQuad.findTopQuad();
if (quad != null)
return quad.getQuad(2);
} else if (quadrant == 3) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getTopQuad(this);
else
quad = pQuad.findTopQuad();
TerrainQuad quad = pQuad.findTopQuad();
if (quad != null)
return quad.getQuad(4);
} else if (quadrant == 0) {
// at the top quad
if (useFinder) {
TerrainQuad quad = neighbourFinder.getTopQuad(this);
return quad;
}
}
return null;
@ -1475,34 +1512,35 @@ public class TerrainQuad extends Node implements Terrain {
protected TerrainQuad findLeftQuad() {
boolean useFinder = false;
if (getParent() == null || !(getParent() instanceof TerrainQuad))
if (neighbourFinder == null)
if (getParent() == null || !(getParent() instanceof TerrainQuad)) {
if (neighbourFinder == null)
return null;
else
useFinder = true;
}
TerrainQuad pQuad = (TerrainQuad) getParent();
TerrainQuad pQuad = null;
if (!useFinder)
pQuad = (TerrainQuad) getParent();
if (quadrant == 3)
return pQuad.getQuad(1);
else if (quadrant == 4)
return pQuad.getQuad(2);
else if (quadrant == 1) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getLeftQuad(this);
else
quad = pQuad.findLeftQuad();
TerrainQuad quad = pQuad.findLeftQuad();
if (quad != null)
return quad.getQuad(3);
} else if (quadrant == 2) {
TerrainQuad quad = null;
if (useFinder)
quad = neighbourFinder.getLeftQuad(this);
else
quad = pQuad.findLeftQuad();
TerrainQuad quad = pQuad.findLeftQuad();
if (quad != null)
return quad.getQuad(4);
} else if (quadrant == 0) {
// at the top quad
if (useFinder) {
TerrainQuad quad = neighbourFinder.getLeftQuad(this);
return quad;
}
}
return null;

@ -49,21 +49,16 @@ public class UpdatedTerrainPatch {
private int previousLod;
private int rightLod,topLod,leftLod,bottomLod;
private IntBuffer newIndexBuffer;
private boolean reIndexNeeded = false;
//private boolean reIndexNeeded = false;
private boolean fixEdges = false;
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {
public UpdatedTerrainPatch(TerrainPatch updatedPatch) {
this.updatedPatch = updatedPatch;
this.newLod = newLod;
}
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod, int prevLOD, boolean reIndexNeeded) {
public UpdatedTerrainPatch(TerrainPatch updatedPatch, int newLod) {
this.updatedPatch = updatedPatch;
this.newLod = newLod;
this.previousLod = prevLOD;
this.reIndexNeeded = reIndexNeeded;
if (this.newLod <= 0)
throw new IllegalArgumentException();
}
public String getName() {
@ -71,7 +66,7 @@ public class UpdatedTerrainPatch {
}
protected boolean lodChanged() {
if (reIndexNeeded && previousLod != newLod)
if ( previousLod != newLod)
return true;
else
return false;
@ -88,16 +83,16 @@ public class UpdatedTerrainPatch {
protected int getNewLod() {
return newLod;
}
public void setNewLod(int newLod) {
this.newLod = newLod;
if (this.newLod < 0)
throw new IllegalArgumentException();
throw new IllegalArgumentException("newLod cannot be less than zero, was: "+newLod);
}
protected IntBuffer getNewIndexBuffer() {
/*protected IntBuffer getNewIndexBuffer() {
return newIndexBuffer;
}
}*/
protected void setNewIndexBuffer(IntBuffer newIndexBuffer) {
this.newIndexBuffer = newIndexBuffer;
@ -144,12 +139,16 @@ public class UpdatedTerrainPatch {
}
public boolean isReIndexNeeded() {
return reIndexNeeded;
if (lodChanged() || isFixEdges())
return true;
//if (leftLod != newLod || rightLod != newLod || bottomLod != newLod || topLod != newLod)
// return true;
return false;
}
public void setReIndexNeeded(boolean reIndexNeeded) {
/*public void setReIndexNeeded(boolean reIndexNeeded) {
this.reIndexNeeded = reIndexNeeded;
}
}*/
public boolean isFixEdges() {
return fixEdges;
@ -159,9 +158,9 @@ public class UpdatedTerrainPatch {
this.fixEdges = fixEdges;
}
public int getPreviousLod() {
/*public int getPreviousLod() {
return previousLod;
}
}*/
public void setPreviousLod(int previousLod) {
this.previousLod = previousLod;
@ -173,11 +172,11 @@ public class UpdatedTerrainPatch {
updatedPatch.setLodTop(topLod);
updatedPatch.setLodLeft(leftLod);
updatedPatch.setLodBottom(bottomLod);
if (newIndexBuffer != null && (reIndexNeeded || fixEdges)) {
if (newIndexBuffer != null && isReIndexNeeded()) {
updatedPatch.setPreviousLod(previousLod);
updatedPatch.getMesh().clearBuffer(Type.Index);
updatedPatch.getMesh().setBuffer(Type.Index, 3, newIndexBuffer);
}
}
}

@ -71,12 +71,12 @@ public class DistanceLodCalculator implements LodCalculator {
int prevLOD = terrainPatch.getLod();
UpdatedTerrainPatch utp = updates.get(terrainPatch.getName());
if (utp == null) {
utp = new UpdatedTerrainPatch(terrainPatch, 0);
utp = new UpdatedTerrainPatch(terrainPatch);
updates.put(utp.getName(), utp);
}
utp.setNewLod(0);
utp.setPreviousLod(prevLOD);
utp.setReIndexNeeded(true);
//utp.setReIndexNeeded(true);
return true;
}
@ -89,15 +89,15 @@ public class DistanceLodCalculator implements LodCalculator {
//System.out.println("lod change: "+lod+" > "+i+" dist: "+distance);
}
int prevLOD = terrainPatch.getLod();
//previousLod = lod;
//lod = i;
UpdatedTerrainPatch utp = updates.get(terrainPatch.getName());
if (utp == null) {
utp = new UpdatedTerrainPatch(terrainPatch, i);//save in here, do not update actual variables
utp = new UpdatedTerrainPatch(terrainPatch);//save in here, do not update actual variables
updates.put(utp.getName(), utp);
}
utp.setNewLod(i);
utp.setPreviousLod(prevLOD);
utp.setReIndexNeeded(reIndexNeeded);
//utp.setReIndexNeeded(reIndexNeeded);
return reIndexNeeded;
}

@ -106,15 +106,15 @@ public class PerspectiveLodCalculator implements LodCalculator {
}
int prevLOD = patch.getLod();
//previousLod = lod;
//lod = i;
UpdatedTerrainPatch utp = updates.get(patch.getName());
if (utp == null) {
utp = new UpdatedTerrainPatch(patch, i);//save in here, do not update actual variables
utp = new UpdatedTerrainPatch(patch);//save in here, do not update actual variables
updates.put(utp.getName(), utp);
}
utp.setNewLod(i);
utp.setPreviousLod(prevLOD);
utp.setReIndexNeeded(reIndexNeeded);
//utp.setReIndexNeeded(reIndexNeeded);
return reIndexNeeded;
}
}

@ -15,11 +15,13 @@ import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Sphere;
import com.jme3.terrain.ProgressMonitor;
import com.jme3.terrain.Terrain;
import com.jme3.terrain.geomipmap.MultiTerrainLodControl;
import com.jme3.terrain.geomipmap.NeighbourFinder;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.texture.Texture;
@ -29,7 +31,8 @@ import java.util.List;
/**
* Demonstrates the NeighbourFinder interface for TerrainQuads,
* allowing you to tile terrains together without having to use
* TerrainGrid.
* TerrainGrid. It also introduces the MultiTerrainLodControl that
* will seam the edges of all the terrains supplied.
*
* @author sploreg
*/
@ -75,7 +78,31 @@ public class TerrainTestTile extends SimpleApplication {
rootNode.addLight(ambLight);
cam.setLocation(new Vector3f(0, 256, 0));
cam.lookAtDirection(new Vector3f(0, -1f, 0).normalizeLocal(), Vector3f.UNIT_X);
cam.lookAtDirection(new Vector3f(0, -1, -1).normalizeLocal(), Vector3f.UNIT_Y);
Sphere s = new Sphere(12, 12, 3);
Geometry g = new Geometry("marker");
g.setMesh(s);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Red);
g.setMaterial(mat);
g.setLocalTranslation(0, -100, 0);
rootNode.attachChild(g);
Geometry g2 = new Geometry("marker");
g2.setMesh(s);
mat.setColor("Color", ColorRGBA.Red);
g2.setMaterial(mat);
g2.setLocalTranslation(10, -100, 0);
rootNode.attachChild(g2);
Geometry g3 = new Geometry("marker");
g3.setMesh(s);
mat.setColor("Color", ColorRGBA.Red);
g3.setMaterial(mat);
g3.setLocalTranslation(0, -100, 10);
rootNode.attachChild(g3);
}
public void loadHintText() {
@ -110,6 +137,8 @@ public class TerrainTestTile extends SimpleApplication {
* the use of NeighbourFinder.
* It just links up the left,right,top,bottom TerrainQuads
* so LOD can work.
* It does not implement many of the Terrain interface's methods,
* you will want to do that for your own implementations.
*/
private class TiledTerrain extends Node implements Terrain, NeighbourFinder {
@ -132,41 +161,43 @@ public class TerrainTestTile extends SimpleApplication {
matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
// CREATE THE TERRAIN
terrain1 = new TerrainQuad("terrain", 65, 513, null);
TerrainLodControl control1 = new TerrainLodControl(terrain1, getCamera());
control1.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain1.addControl(control1);
terrain1 = new TerrainQuad("terrain 1", 65, 513, null);
terrain1.setMaterial(matTerrain);
terrain1.setLocalTranslation(-256, -100, -256);
terrain1.setLocalScale(1f, 1f, 1f);
this.attachChild(terrain1);
terrain2 = new TerrainQuad("terrain", 65, 513, null);
TerrainLodControl control2 = new TerrainLodControl(terrain2, getCamera());
control2.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain2.addControl(control2);
terrain2 = new TerrainQuad("terrain 2", 65, 513, null);
terrain2.setMaterial(matTerrain);
terrain2.setLocalTranslation(-256, -100, 256);
terrain2.setLocalScale(1f, 1f, 1f);
this.attachChild(terrain2);
terrain3 = new TerrainQuad("terrain", 65, 513, null);
TerrainLodControl control3 = new TerrainLodControl(terrain3, getCamera());
control3.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain3.addControl(control3);
terrain3 = new TerrainQuad("terrain 3", 65, 513, null);
terrain3.setMaterial(matTerrain);
terrain3.setLocalTranslation(256, -100, -256);
terrain3.setLocalScale(1f, 1f, 1f);
this.attachChild(terrain3);
terrain4 = new TerrainQuad("terrain", 65, 513, null);
TerrainLodControl control4 = new TerrainLodControl(terrain4, getCamera());
control4.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
terrain4.addControl(control4);
terrain4 = new TerrainQuad("terrain 4", 65, 513, null);
terrain4.setMaterial(matTerrain);
terrain4.setLocalTranslation(256, -100, 256);
terrain4.setLocalScale(1f, 1f, 1f);
this.attachChild(terrain4);
terrain1.setNeighbourFinder(this);
terrain2.setNeighbourFinder(this);
terrain3.setNeighbourFinder(this);
terrain4.setNeighbourFinder(this);
MultiTerrainLodControl lodControl = new MultiTerrainLodControl(getCamera());
lodControl.setLodCalculator( new DistanceLodCalculator(65, 2.7f) ); // patch size, and a multiplier
lodControl.addTerrain(terrain1);
lodControl.addTerrain(terrain2);
lodControl.addTerrain(terrain3);// order of these seems to matter
lodControl.addTerrain(terrain4);
this.addControl(lodControl);
}
/**
@ -174,6 +205,7 @@ public class TerrainTestTile extends SimpleApplication {
* 2 4
*/
public TerrainQuad getRightQuad(TerrainQuad center) {
//System.out.println("lookup neighbour");
if (center == terrain1)
return terrain3;
if (center == terrain2)
@ -187,6 +219,7 @@ public class TerrainTestTile extends SimpleApplication {
* 2 4
*/
public TerrainQuad getLeftQuad(TerrainQuad center) {
//System.out.println("lookup neighbour");
if (center == terrain3)
return terrain1;
if (center == terrain4)
@ -200,6 +233,7 @@ public class TerrainTestTile extends SimpleApplication {
* 2 4
*/
public TerrainQuad getTopQuad(TerrainQuad center) {
//System.out.println("lookup neighbour");
if (center == terrain2)
return terrain1;
if (center == terrain4)
@ -213,6 +247,7 @@ public class TerrainTestTile extends SimpleApplication {
* 2 4
*/
public TerrainQuad getDownQuad(TerrainQuad center) {
//System.out.println("lookup neighbour");
if (center == terrain1)
return terrain2;
if (center == terrain3)

Loading…
Cancel
Save