|
@@ -44,20 +44,19 @@ import com.jme3.scene.Spatial;
|
|
import com.jme3.scene.control.AbstractControl;
|
|
import com.jme3.scene.control.AbstractControl;
|
|
import com.jme3.scene.control.Control;
|
|
import com.jme3.scene.control.Control;
|
|
import com.jme3.terrain.Terrain;
|
|
import com.jme3.terrain.Terrain;
|
|
|
|
+import com.jme3.terrain.executor.TerrainExecutorService;
|
|
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
|
|
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
|
|
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
|
|
import com.jme3.terrain.geomipmap.lodcalc.LodCalculator;
|
|
|
|
+import com.jme3.util.SafeArrayList;
|
|
import com.jme3.util.clone.Cloner;
|
|
import com.jme3.util.clone.Cloner;
|
|
-import com.jme3.util.clone.JmeCloneable;
|
|
|
|
|
|
+
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.concurrent.Callable;
|
|
import java.util.concurrent.ExecutionException;
|
|
import java.util.concurrent.ExecutionException;
|
|
-import java.util.concurrent.ExecutorService;
|
|
|
|
-import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.Future;
|
|
import java.util.concurrent.Future;
|
|
-import java.util.concurrent.ThreadFactory;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import java.util.logging.Logger;
|
|
@@ -84,32 +83,68 @@ import java.util.logging.Logger;
|
|
*/
|
|
*/
|
|
public class TerrainLodControl extends AbstractControl {
|
|
public class TerrainLodControl extends AbstractControl {
|
|
|
|
|
|
- private Terrain terrain;
|
|
|
|
- protected List<Camera> cameras;
|
|
|
|
- private List<Vector3f> cameraLocations = new ArrayList<Vector3f>();
|
|
|
|
- protected LodCalculator lodCalculator;
|
|
|
|
- private boolean hasResetLod = false; // used when enabled is set to false
|
|
|
|
|
|
+ /**
|
|
|
|
+ * The list of cameras for when terrain supports multiple cameras (ie split screen)
|
|
|
|
+ */
|
|
|
|
+ protected SafeArrayList<Camera> cameras;
|
|
|
|
+ protected SafeArrayList<Vector3f> cameraLocations;
|
|
|
|
+ protected SafeArrayList<Vector3f> lastCameraLocations;
|
|
|
|
|
|
- private HashMap<String,UpdatedTerrainPatch> updatedPatches;
|
|
|
|
- private final Object updatePatchesLock = new Object();
|
|
|
|
|
|
+ protected AtomicBoolean lodCalcRunning;
|
|
|
|
|
|
- protected List<Vector3f> lastCameraLocations; // used for LOD calc
|
|
|
|
- private AtomicBoolean lodCalcRunning = new AtomicBoolean(false);
|
|
|
|
- private int lodOffCount = 0;
|
|
|
|
|
|
+ /**
|
|
|
|
+ * The previous location of {@link #camera}.
|
|
|
|
+ */
|
|
|
|
+ protected Vector3f previousCameraLocation;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The camera from render view port.
|
|
|
|
+ */
|
|
|
|
+ protected Camera camera;
|
|
|
|
|
|
- protected ExecutorService executor;
|
|
|
|
|
|
+ protected Terrain terrain;
|
|
|
|
+ protected LodCalculator lodCalculator;
|
|
protected Future<HashMap<String, UpdatedTerrainPatch>> indexer;
|
|
protected Future<HashMap<String, UpdatedTerrainPatch>> indexer;
|
|
- private boolean forceUpdate = true;
|
|
|
|
|
|
+
|
|
|
|
+ private int lodOffCount;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The flag of using a camera from render viewport instead cameras from {@link #cameras}.
|
|
|
|
+ */
|
|
|
|
+ protected boolean useRenderCamera;
|
|
|
|
+
|
|
|
|
+ protected boolean forceUpdate;
|
|
|
|
+ protected boolean hasResetLod; // used when enabled is set to false
|
|
|
|
|
|
public TerrainLodControl() {
|
|
public TerrainLodControl() {
|
|
|
|
+ hasResetLod = false;
|
|
|
|
+ forceUpdate = true;
|
|
|
|
+ previousCameraLocation = new Vector3f();
|
|
|
|
+ cameras = new SafeArrayList<>(Camera.class);
|
|
|
|
+ cameraLocations = new SafeArrayList<>(Vector3f.class);
|
|
|
|
+ lastCameraLocations = new SafeArrayList<>(Vector3f.class);
|
|
|
|
+ lodCalcRunning = new AtomicBoolean(false);
|
|
|
|
+ lodOffCount = 0;
|
|
|
|
+ lodCalculator = makeLodCalculator(); // a default calculator
|
|
}
|
|
}
|
|
|
|
|
|
- public TerrainLodControl(Terrain terrain, Camera camera) {
|
|
|
|
- List<Camera> cams = new ArrayList<Camera>();
|
|
|
|
- cams.add(camera);
|
|
|
|
|
|
+ protected DistanceLodCalculator makeLodCalculator() {
|
|
|
|
+ return new DistanceLodCalculator(65, 2.7f);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public TerrainLodControl(final Terrain terrain) {
|
|
|
|
+ this();
|
|
this.terrain = terrain;
|
|
this.terrain = terrain;
|
|
- this.cameras = cams;
|
|
|
|
- lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public TerrainLodControl(final Camera camera) {
|
|
|
|
+ this();
|
|
|
|
+ setCamera(camera);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public TerrainLodControl(final Terrain terrain, final Camera camera) {
|
|
|
|
+ this(terrain);
|
|
|
|
+ setCamera(camera);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -117,41 +152,44 @@ public class TerrainLodControl extends AbstractControl {
|
|
* @param terrain to act upon (must be a Spatial)
|
|
* @param terrain to act upon (must be a Spatial)
|
|
* @param cameras one or more cameras to reference for LOD calc
|
|
* @param cameras one or more cameras to reference for LOD calc
|
|
*/
|
|
*/
|
|
- public TerrainLodControl(Terrain terrain, List<Camera> cameras) {
|
|
|
|
- this.terrain = terrain;
|
|
|
|
- this.cameras = cameras;
|
|
|
|
- lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator
|
|
|
|
|
|
+ public TerrainLodControl(final Terrain terrain, final List<Camera> cameras) {
|
|
|
|
+ this(terrain);
|
|
|
|
+ setCameras(cameras);
|
|
}
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
|
- protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
|
|
|
|
+ /**
|
|
|
|
+ * @param useRenderCamera true if need to use a camera from render view port.
|
|
|
|
+ */
|
|
|
|
+ public void setUseRenderCamera(final boolean useRenderCamera) {
|
|
|
|
+ this.useRenderCamera = useRenderCamera;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Set your own custom executor to be used. The control will use
|
|
|
|
- * this instead of creating its own.
|
|
|
|
|
|
+ * @return true if need to use a camera from render view port.
|
|
*/
|
|
*/
|
|
- public void setExecutor(ExecutorService executor) {
|
|
|
|
- this.executor = executor;
|
|
|
|
|
|
+ public boolean isUseRenderCamera() {
|
|
|
|
+ return useRenderCamera;
|
|
}
|
|
}
|
|
|
|
|
|
- protected ExecutorService createExecutorService() {
|
|
|
|
- return Executors.newSingleThreadExecutor(new ThreadFactory() {
|
|
|
|
- public Thread newThread(Runnable r) {
|
|
|
|
- Thread th = new Thread(r);
|
|
|
|
- th.setName("jME3 Terrain Thread");
|
|
|
|
- th.setDaemon(true);
|
|
|
|
- return th;
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ @Override
|
|
|
|
+ protected void controlRender(final RenderManager rm, final ViewPort vp) {
|
|
|
|
+
|
|
|
|
+ if (!isUseRenderCamera()) {
|
|
|
|
+ return;
|
|
|
|
+ } else if (camera == vp.getCamera()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ camera = vp.getCamera();
|
|
|
|
+ previousCameraLocation.set(camera.getLocation());
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
protected void controlUpdate(float tpf) {
|
|
protected void controlUpdate(float tpf) {
|
|
- //list of cameras for when terrain supports multiple cameras (ie split screen)
|
|
|
|
|
|
|
|
- if (lodCalculator == null)
|
|
|
|
|
|
+ if (lodCalculator == null) {
|
|
return;
|
|
return;
|
|
|
|
+ }
|
|
|
|
|
|
if (!enabled) {
|
|
if (!enabled) {
|
|
if (!hasResetLod) {
|
|
if (!hasResetLod) {
|
|
@@ -161,12 +199,26 @@ public class TerrainLodControl extends AbstractControl {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if (cameras != null) {
|
|
|
|
- cameraLocations.clear();
|
|
|
|
- for (Camera c : cameras) // populate them
|
|
|
|
- {
|
|
|
|
- cameraLocations.add(c.getLocation());
|
|
|
|
|
|
+ // if we use a camera from render
|
|
|
|
+ if (isUseRenderCamera()) {
|
|
|
|
+ updateLOD(lodCalculator);
|
|
|
|
+ }
|
|
|
|
+ // if we use set cameras
|
|
|
|
+ else if (!cameras.isEmpty()) {
|
|
|
|
+
|
|
|
|
+ // need to have count of positions the same with count of cameras
|
|
|
|
+ if (cameraLocations.size() != cameras.size()) {
|
|
|
|
+ cameraLocations.clear();
|
|
|
|
+ for (int i = 0; i < cameras.size(); i++) {
|
|
|
|
+ cameraLocations.add(new Vector3f());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // we need to update current camera positions
|
|
|
|
+ for (int i = 0; i < cameras.size(); i++) {
|
|
|
|
+ cameraLocations.get(i).set(cameras.get(i).getLocation());
|
|
}
|
|
}
|
|
|
|
+
|
|
updateLOD(cameraLocations, lodCalculator);
|
|
updateLOD(cameraLocations, lodCalculator);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -176,53 +228,110 @@ public class TerrainLodControl extends AbstractControl {
|
|
* It will clear up any threads it had.
|
|
* It will clear up any threads it had.
|
|
*/
|
|
*/
|
|
public void detachAndCleanUpControl() {
|
|
public void detachAndCleanUpControl() {
|
|
- if (executor != null)
|
|
|
|
- executor.shutdownNow();
|
|
|
|
|
|
+
|
|
|
|
+ if (indexer != null) {
|
|
|
|
+ indexer.cancel(true);
|
|
|
|
+ indexer = null;
|
|
|
|
+ }
|
|
|
|
+
|
|
getSpatial().removeControl(this);
|
|
getSpatial().removeControl(this);
|
|
}
|
|
}
|
|
|
|
|
|
// do all of the LOD calculations
|
|
// do all of the LOD calculations
|
|
- protected void updateLOD(List<Vector3f> locations, LodCalculator lodCalculator) {
|
|
|
|
- if(getSpatial() == null){
|
|
|
|
|
|
+ protected void updateLOD(final LodCalculator lodCalculator) {
|
|
|
|
+
|
|
|
|
+ if (getSpatial() == null || camera == null) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// update any existing ones that need updating
|
|
// update any existing ones that need updating
|
|
updateQuadLODs();
|
|
updateQuadLODs();
|
|
|
|
|
|
- if (lodCalculator.isLodOff()) {
|
|
|
|
- // we want to calculate the base lod at least once
|
|
|
|
- if (lodOffCount == 1)
|
|
|
|
- return;
|
|
|
|
- else
|
|
|
|
- lodOffCount++;
|
|
|
|
- } else
|
|
|
|
- lodOffCount = 0;
|
|
|
|
|
|
+ if (updateLodOffCount(lodCalculator)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ final Vector3f currentLocation = camera.getLocation();
|
|
|
|
|
|
- if (lastCameraLocations != null) {
|
|
|
|
- if (!forceUpdate && lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())
|
|
|
|
- return; // don't update if in same spot
|
|
|
|
- else
|
|
|
|
- lastCameraLocations = cloneVectorList(locations);
|
|
|
|
- forceUpdate = false;
|
|
|
|
|
|
+ if (!forceUpdate && previousCameraLocation.equals(currentLocation) && !lodCalculator.isLodOff()) {
|
|
|
|
+ return; // don't update if in same spot
|
|
|
|
+ } else {
|
|
|
|
+ previousCameraLocation.set(currentLocation);
|
|
}
|
|
}
|
|
- else {
|
|
|
|
- lastCameraLocations = cloneVectorList(locations);
|
|
|
|
|
|
+
|
|
|
|
+ forceUpdate = false;
|
|
|
|
+
|
|
|
|
+ if (!lodCalcRunning.compareAndSet(false, true)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ prepareTerrain();
|
|
|
|
+
|
|
|
|
+ final SafeArrayList<Vector3f> locations = new SafeArrayList<>(Vector3f.class, 1);
|
|
|
|
+ locations.add(currentLocation);
|
|
|
|
+
|
|
|
|
+ final TerrainExecutorService executorService = TerrainExecutorService.getInstance();
|
|
|
|
+ indexer = executorService.submit(createLodUpdateTask(locations, lodCalculator));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // do all of the LOD calculations
|
|
|
|
+ protected void updateLOD(final SafeArrayList<Vector3f> locations, final LodCalculator lodCalculator) {
|
|
|
|
+
|
|
|
|
+ if (getSpatial() == null || locations.isEmpty()) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (isLodCalcRunning()) {
|
|
|
|
|
|
+ // update any existing ones that need updating
|
|
|
|
+ updateQuadLODs();
|
|
|
|
+
|
|
|
|
+ if (updateLodOffCount(lodCalculator)) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- setLodCalcRunning(true);
|
|
|
|
|
|
|
|
- if (executor == null)
|
|
|
|
- executor = createExecutorService();
|
|
|
|
|
|
+ if (!forceUpdate && locations.equals(lastCameraLocations) && !lodCalculator.isLodOff()) {
|
|
|
|
+ return; // don't update if in same spot
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ // need to have count of last camera locations the same with count of locations
|
|
|
|
+ if (lastCameraLocations.size() != locations.size()) {
|
|
|
|
+ lastCameraLocations.clear();
|
|
|
|
+ for (int i = 0; i < locations.size(); i++) {
|
|
|
|
+ lastCameraLocations.add(new Vector3f());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // we need to update last camera locations to current
|
|
|
|
+ for (int i = 0; i < locations.size(); i++) {
|
|
|
|
+ lastCameraLocations.get(i).set(locations.get(i));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ forceUpdate = false;
|
|
|
|
+
|
|
|
|
+ if (!lodCalcRunning.compareAndSet(false, true)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
prepareTerrain();
|
|
prepareTerrain();
|
|
|
|
|
|
- UpdateLOD updateLodThread = getLodThread(locations, lodCalculator);
|
|
|
|
- indexer = executor.submit(updateLodThread);
|
|
|
|
|
|
+ final TerrainExecutorService executorService = TerrainExecutorService.getInstance();
|
|
|
|
+ indexer = executorService.submit(createLodUpdateTask(cloneVectorList(locations), lodCalculator));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected boolean updateLodOffCount(final LodCalculator lodCalculator) {
|
|
|
|
+
|
|
|
|
+ if (lodCalculator.isLodOff()) {
|
|
|
|
+ // we want to calculate the base lod at least once
|
|
|
|
+ if (lodOffCount == 1) {
|
|
|
|
+ return true;
|
|
|
|
+ } else {
|
|
|
|
+ lodOffCount++;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ lodOffCount = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -234,11 +343,12 @@ public class TerrainLodControl extends AbstractControl {
|
|
}
|
|
}
|
|
|
|
|
|
protected void prepareTerrain() {
|
|
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
|
|
|
|
|
|
+ TerrainQuad terrain = (TerrainQuad) getSpatial();
|
|
|
|
+ // cache the terrain's world transforms so they can be accessed on the separate thread safely
|
|
|
|
+ terrain.cacheTerrainTransforms();
|
|
}
|
|
}
|
|
|
|
|
|
- protected UpdateLOD getLodThread(List<Vector3f> locations, LodCalculator lodCalculator) {
|
|
|
|
|
|
+ protected UpdateLOD createLodUpdateTask(final SafeArrayList<Vector3f> locations, final LodCalculator lodCalculator) {
|
|
return new UpdateLOD(locations, lodCalculator);
|
|
return new UpdateLOD(locations, lodCalculator);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -246,115 +356,78 @@ public class TerrainLodControl extends AbstractControl {
|
|
* Back on the ogl thread: update the terrain patch geometries
|
|
* Back on the ogl thread: update the terrain patch geometries
|
|
*/
|
|
*/
|
|
private void updateQuadLODs() {
|
|
private void updateQuadLODs() {
|
|
- 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;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ if (indexer == null || !indexer.isDone()) {
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
- 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;
|
|
|
|
|
|
+ try {
|
|
|
|
+
|
|
|
|
+ final HashMap<String, UpdatedTerrainPatch> updated = indexer.get();
|
|
|
|
+ if (updated != null) {
|
|
|
|
+ // do the actual geometry update here
|
|
|
|
+ for (final UpdatedTerrainPatch utp : updated.values()) {
|
|
|
|
+ utp.updateAll();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ } catch (final InterruptedException | ExecutionException ex) {
|
|
|
|
+ Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex);
|
|
|
|
+ } finally {
|
|
|
|
+ indexer = null;
|
|
}
|
|
}
|
|
- return theSame;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- protected synchronized boolean isLodCalcRunning() {
|
|
|
|
- return lodCalcRunning.get();
|
|
|
|
- }
|
|
|
|
|
|
+ private SafeArrayList<Vector3f> cloneVectorList(SafeArrayList<Vector3f> locations) {
|
|
|
|
|
|
- protected synchronized void setLodCalcRunning(boolean running) {
|
|
|
|
- lodCalcRunning.set(running);
|
|
|
|
- }
|
|
|
|
|
|
+ final SafeArrayList<Vector3f> cloned = new SafeArrayList<>(Vector3f.class, locations.size());
|
|
|
|
+
|
|
|
|
+ for (final Vector3f location : locations.getArray()) {
|
|
|
|
+ cloned.add(location.clone());
|
|
|
|
+ }
|
|
|
|
|
|
- private List<Vector3f> cloneVectorList(List<Vector3f> locations) {
|
|
|
|
- List<Vector3f> cloned = new ArrayList<Vector3f>();
|
|
|
|
- for(Vector3f l : locations)
|
|
|
|
- cloned.add(l.clone());
|
|
|
|
return cloned;
|
|
return cloned;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
@Override
|
|
@Override
|
|
public Object jmeClone() {
|
|
public Object jmeClone() {
|
|
- if (spatial instanceof Terrain) {
|
|
|
|
- TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, cameras);
|
|
|
|
- cloned.setLodCalculator(lodCalculator.clone());
|
|
|
|
- cloned.spatial = spatial;
|
|
|
|
- return cloned;
|
|
|
|
|
|
+ try {
|
|
|
|
+ return super.clone();
|
|
|
|
+ } catch (final CloneNotSupportedException e) {
|
|
|
|
+ throw new RuntimeException(e);
|
|
}
|
|
}
|
|
- return null;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public void cloneFields( Cloner cloner, Object original ) {
|
|
|
|
|
|
+ public void cloneFields(final Cloner cloner, final Object original) {
|
|
super.cloneFields(cloner, original);
|
|
super.cloneFields(cloner, original);
|
|
|
|
|
|
this.lodCalculator = cloner.clone(lodCalculator);
|
|
this.lodCalculator = cloner.clone(lodCalculator);
|
|
-
|
|
|
|
- try {
|
|
|
|
- // Not deep clone of the cameras themselves
|
|
|
|
- this.cameras = cloner.javaClone(cameras);
|
|
|
|
- } catch( CloneNotSupportedException e ) {
|
|
|
|
- throw new RuntimeException("Error cloning", e);
|
|
|
|
- }
|
|
|
|
|
|
+ this.cameras = new SafeArrayList<>(Camera.class, cameras);
|
|
|
|
+ this.cameraLocations = new SafeArrayList<>(Vector3f.class);
|
|
|
|
+ this.lastCameraLocations = new SafeArrayList<>(Vector3f.class);
|
|
|
|
+ this.lodCalcRunning = new AtomicBoolean();
|
|
|
|
+ this.previousCameraLocation = new Vector3f();
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
@Override
|
|
@Override
|
|
- public Control cloneForSpatial(Spatial spatial) {
|
|
|
|
|
|
+ public Control cloneForSpatial(final Spatial spatial) {
|
|
if (spatial instanceof Terrain) {
|
|
if (spatial instanceof Terrain) {
|
|
- List<Camera> cameraClone = new ArrayList<Camera>();
|
|
|
|
- if (cameras != null) {
|
|
|
|
- for (Camera c : cameras) {
|
|
|
|
- cameraClone.add(c);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, cameraClone);
|
|
|
|
|
|
+ TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, new ArrayList<>(cameras));
|
|
cloned.setLodCalculator(lodCalculator.clone());
|
|
cloned.setLodCalculator(lodCalculator.clone());
|
|
return cloned;
|
|
return cloned;
|
|
}
|
|
}
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
- public void setCamera(Camera camera) {
|
|
|
|
- List<Camera> cams = new ArrayList<Camera>();
|
|
|
|
- cams.add(camera);
|
|
|
|
- setCameras(cams);
|
|
|
|
|
|
+ public void setCamera(final Camera camera) {
|
|
|
|
+ this.cameras.clear();
|
|
|
|
+ this.cameras.add(camera);
|
|
}
|
|
}
|
|
|
|
|
|
- public void setCameras(List<Camera> cameras) {
|
|
|
|
- this.cameras = cameras;
|
|
|
|
- cameraLocations.clear();
|
|
|
|
- for (Camera c : cameras) {
|
|
|
|
- cameraLocations.add(c.getLocation());
|
|
|
|
- }
|
|
|
|
|
|
+ public void setCameras(final List<Camera> cameras) {
|
|
|
|
+ this.cameras.clear();
|
|
|
|
+ this.cameras.addAll(cameras);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -373,7 +446,7 @@ public class TerrainLodControl extends AbstractControl {
|
|
return lodCalculator;
|
|
return lodCalculator;
|
|
}
|
|
}
|
|
|
|
|
|
- public void setLodCalculator(LodCalculator lodCalculator) {
|
|
|
|
|
|
+ public void setLodCalculator(final LodCalculator lodCalculator) {
|
|
this.lodCalculator = lodCalculator;
|
|
this.lodCalculator = lodCalculator;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -393,64 +466,60 @@ public class TerrainLodControl extends AbstractControl {
|
|
/**
|
|
/**
|
|
* Calculates the LOD of all child terrain patches.
|
|
* Calculates the LOD of all child terrain patches.
|
|
*/
|
|
*/
|
|
- protected class UpdateLOD implements Callable<HashMap<String,UpdatedTerrainPatch>> {
|
|
|
|
- protected List<Vector3f> camLocations;
|
|
|
|
- protected LodCalculator lodCalculator;
|
|
|
|
|
|
+ protected class UpdateLOD implements Callable<HashMap<String, UpdatedTerrainPatch>> {
|
|
|
|
|
|
- protected UpdateLOD(List<Vector3f> camLocations, LodCalculator lodCalculator) {
|
|
|
|
|
|
+ protected final SafeArrayList<Vector3f> camLocations;
|
|
|
|
+ protected final LodCalculator lodCalculator;
|
|
|
|
+
|
|
|
|
+ protected UpdateLOD(final SafeArrayList<Vector3f> camLocations, final LodCalculator lodCalculator) {
|
|
this.camLocations = camLocations;
|
|
this.camLocations = camLocations;
|
|
this.lodCalculator = lodCalculator;
|
|
this.lodCalculator = lodCalculator;
|
|
}
|
|
}
|
|
|
|
|
|
public HashMap<String, UpdatedTerrainPatch> call() throws Exception {
|
|
public HashMap<String, UpdatedTerrainPatch> call() throws Exception {
|
|
- //long start = System.currentTimeMillis();
|
|
|
|
- //if (isLodCalcRunning()) {
|
|
|
|
- // return null;
|
|
|
|
- //}
|
|
|
|
- setLodCalcRunning(true);
|
|
|
|
|
|
|
|
- TerrainQuad terrainQuad = (TerrainQuad)getSpatial();
|
|
|
|
|
|
+ TerrainQuad terrainQuad = (TerrainQuad) getSpatial();
|
|
|
|
|
|
// go through each patch and calculate its LOD based on camera distance
|
|
// 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
|
|
|
|
|
|
+ HashMap<String, UpdatedTerrainPatch> updated = new HashMap<>();
|
|
|
|
+ // 'updated' gets populated here
|
|
|
|
+ boolean lodChanged = terrainQuad.calculateLod(camLocations, updated, lodCalculator);
|
|
|
|
|
|
if (!lodChanged) {
|
|
if (!lodChanged) {
|
|
// not worth updating anything else since no one's LOD changed
|
|
// not worth updating anything else since no one's LOD changed
|
|
- setLodCalcRunning(false);
|
|
|
|
|
|
+ lodCalcRunning.set(false);
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
// then calculate its neighbour LOD values for seaming in the shader
|
|
// then calculate its neighbour LOD values for seaming in the shader
|
|
terrainQuad.findNeighboursLod(updated);
|
|
terrainQuad.findNeighboursLod(updated);
|
|
-
|
|
|
|
- terrainQuad.fixEdges(updated); // 'updated' can get added to here
|
|
|
|
-
|
|
|
|
|
|
+ // 'updated' can get added to here
|
|
|
|
+ terrainQuad.fixEdges(updated);
|
|
terrainQuad.reIndexPages(updated, lodCalculator.usesVariableLod());
|
|
terrainQuad.reIndexPages(updated, lodCalculator.usesVariableLod());
|
|
|
|
|
|
//setUpdateQuadLODs(updated); // set back to main ogl thread
|
|
//setUpdateQuadLODs(updated); // set back to main ogl thread
|
|
|
|
|
|
- setLodCalcRunning(false);
|
|
|
|
|
|
+ lodCalcRunning.set(false);
|
|
|
|
|
|
return updated;
|
|
return updated;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public void write(JmeExporter ex) throws IOException {
|
|
|
|
|
|
+ public void write(final JmeExporter ex) throws IOException {
|
|
super.write(ex);
|
|
super.write(ex);
|
|
OutputCapsule oc = ex.getCapsule(this);
|
|
OutputCapsule oc = ex.getCapsule(this);
|
|
oc.write((Node)terrain, "terrain", null);
|
|
oc.write((Node)terrain, "terrain", null);
|
|
oc.write(lodCalculator, "lodCalculator", null);
|
|
oc.write(lodCalculator, "lodCalculator", null);
|
|
|
|
+ oc.write(useRenderCamera, "useRenderCamera", false);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public void read(JmeImporter im) throws IOException {
|
|
|
|
|
|
+ public void read(final JmeImporter im) throws IOException {
|
|
super.read(im);
|
|
super.read(im);
|
|
InputCapsule ic = im.getCapsule(this);
|
|
InputCapsule ic = im.getCapsule(this);
|
|
terrain = (Terrain) ic.readSavable("terrain", null);
|
|
terrain = (Terrain) ic.readSavable("terrain", null);
|
|
lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
|
|
lodCalculator = (LodCalculator) ic.readSavable("lodCalculator", new DistanceLodCalculator());
|
|
|
|
+ useRenderCamera = ic.readBoolean("useRenderCamera", false);
|
|
}
|
|
}
|
|
-
|
|
|
|
}
|
|
}
|