diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java index 2f85d23c4..9cd9e11d2 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.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()); } } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/MultiTerrainLodControl.java b/engine/src/terrain/com/jme3/terrain/geomipmap/MultiTerrainLodControl.java new file mode 100644 index 000000000..ebe694a4d --- /dev/null +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/MultiTerrainLodControl.java @@ -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 terrains = new ArrayList(); + private List addedTerrains = new ArrayList(); + private List removedTerrains = new ArrayList(); + + public MultiTerrainLodControl(List cameras) { + this.cameras = cameras; + lodCalculator = new DistanceLodCalculator(65, 2.7f); + } + + public MultiTerrainLodControl(Camera camera) { + List cams = new ArrayList(); + 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 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 camLocations, LodCalculator lodCalculator) { + super(camLocations, lodCalculator); + } + + @Override + public HashMap call() throws Exception { + + setLodCalcRunning(true); + + HashMap updated = new HashMap(); + + 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; + } + } +} diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java b/engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java index a6cc2c864..66777b8fe 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/NeighbourFinder.java @@ -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); } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java index 25b17d47b..e1f96e1fe 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java @@ -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 cameras; + protected List cameras; private List cameraLocations = new ArrayList(); - private LodCalculator lodCalculator; + protected LodCalculator lodCalculator; private boolean hasResetLod = false; // used when enabled is set to false private HashMap updatedPatches; private final Object updatePatchesLock = new Object(); protected List lastCameraLocations; // used for LOD calc - private boolean lodCalcRunning = false; + private AtomicBoolean lodCalcRunning = new AtomicBoolean(false); private int lodOffCount = 0; protected ExecutorService executor; + protected Future> 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 updated) { - synchronized (updatePatchesLock) { - updatedPatches = updated; - } + protected UpdateLOD getLodThread(List 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 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 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 cloneVectorList(List 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 camLocations; - private LodCalculator lodCalculator; + protected class UpdateLOD implements Callable> { + protected List camLocations; + protected LodCalculator lodCalculator; - UpdateLOD(List camLocations, LodCalculator lodCalculator) { + protected UpdateLOD(List 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 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; } } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java index 0745f7051..32daa0749 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java @@ -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(); diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java index fc0e15772..c44d4a335 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java @@ -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; diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java index f64ee1cc4..e9bb389d5 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java @@ -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); } } - + } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java index 86bb38d91..412658c5e 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java @@ -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; } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java index 312b17d7f..834695f47 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/PerspectiveLodCalculator.java @@ -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; } } diff --git a/engine/src/test/jme3test/terrain/TerrainTestTile.java b/engine/src/test/jme3test/terrain/TerrainTestTile.java index 73798c95c..11d19bcbb 100644 --- a/engine/src/test/jme3test/terrain/TerrainTestTile.java +++ b/engine/src/test/jme3test/terrain/TerrainTestTile.java @@ -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)