Browse Source

* 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

bre..ns 13 years ago
parent
commit
a1249b551d

+ 0 - 11
engine/src/terrain/com/jme3/terrain/Terrain.java

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

+ 21 - 51
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainGrid.java

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

+ 180 - 5
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainLodControl.java

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

+ 19 - 0
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java

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

+ 12 - 191
engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java

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

+ 102 - 102
engine/src/terrain/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java

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

+ 4 - 4
engine/src/terrain/com/jme3/terrain/geomipmap/lodcalc/DistanceLodCalculator.java

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

+ 2 - 1
engine/src/test/jme3test/terrain/TerrainFractalGridTest.java

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

+ 2 - 3
engine/src/test/jme3test/terrain/TerrainGridAlphaMapTest.java

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

+ 2 - 1
engine/src/test/jme3test/terrain/TerrainGridSerializationTest.java

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

+ 2 - 1
engine/src/test/jme3test/terrain/TerrainGridTest.java

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

+ 2 - 1
engine/src/test/jme3test/terrain/TerrainGridTileLoaderTest.java

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