Converted spatial over to use Cloner to do its various

deep and semi-shallow cloning.  I'd be very surprised if nothing
is broken as there is only so much testing I can easily do.
Also various fixes for places I forgot to call super.cloneFields().
cleanup_build_scripts
Paul Speed 9 years ago
parent 0a876b04d2
commit ab6fb03171
  1. 29
      jme3-core/src/main/java/com/jme3/animation/EffectTrack.java
  2. 2
      jme3-core/src/main/java/com/jme3/audio/AudioNode.java
  3. 9
      jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java
  4. 2
      jme3-core/src/main/java/com/jme3/effect/influencers/RadialParticleInfluencer.java
  5. 2
      jme3-core/src/main/java/com/jme3/font/BitmapText.java
  6. 2
      jme3-core/src/main/java/com/jme3/scene/AssetLinkNode.java
  7. 2
      jme3-core/src/main/java/com/jme3/scene/BatchNode.java
  8. 2
      jme3-core/src/main/java/com/jme3/scene/CameraNode.java
  9. 49
      jme3-core/src/main/java/com/jme3/scene/Geometry.java
  10. 2
      jme3-core/src/main/java/com/jme3/scene/LightNode.java
  11. 4
      jme3-core/src/main/java/com/jme3/scene/Mesh.java
  12. 14
      jme3-core/src/main/java/com/jme3/scene/Node.java
  13. 72
      jme3-core/src/main/java/com/jme3/scene/Spatial.java
  14. 2
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java
  15. 2
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java
  16. 1
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/NormalRecalcControl.java
  17. 88
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java
  18. 1
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java
  19. 2
      jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java

@ -118,22 +118,17 @@ public class EffectTrack implements ClonableTrack {
} }
} }
@Override @Override
public Object jmeClone() { public Object jmeClone() {
KillParticleControl c = new KillParticleControl(); KillParticleControl c = new KillParticleControl();
//this control should be removed as it shouldn't have been persisted in the first place //this control should be removed as it shouldn't have been persisted in the first place
//In the quest to find the less hackish solution to achieve this, //In the quest to find the less hackish solution to achieve this,
//making it remove itself from the spatial in the first update loop when loaded was the less bad. //making it remove itself from the spatial in the first update loop when loaded was the less bad.
c.remove = true; c.remove = true;
c.spatial = spatial; c.spatial = spatial;
return c; return c;
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.spatial = cloner.clone(spatial);
} }
@Override @Override
protected void controlRender(RenderManager rm, ViewPort vp) { protected void controlRender(RenderManager rm, ViewPort vp) {
} }
@ -143,8 +138,8 @@ public class EffectTrack implements ClonableTrack {
KillParticleControl c = new KillParticleControl(); KillParticleControl c = new KillParticleControl();
//this control should be removed as it shouldn't have been persisted in the first place //this control should be removed as it shouldn't have been persisted in the first place
//In the quest to find the less hackish solution to achieve this, //In the quest to find the less hackish solution to achieve this,
//making it remove itself from the spatial in the first update loop when loaded was the less bad. //making it remove itself from the spatial in the first update loop when loaded was the less bad.
c.remove = true; c.remove = true;
c.setSpatial(spatial); c.setSpatial(spatial);
return c; return c;
@ -261,7 +256,7 @@ public class EffectTrack implements ClonableTrack {
public float[] getKeyFrameTimes() { public float[] getKeyFrameTimes() {
return new float[] { startOffset }; return new float[] { startOffset };
} }
/** /**
* Clone this track * Clone this track
* *
@ -302,21 +297,21 @@ public class EffectTrack implements ClonableTrack {
return effectTrack; return effectTrack;
} }
@Override @Override
public Object jmeClone() { public Object jmeClone() {
try { try {
return super.clone(); return super.clone();
} catch( CloneNotSupportedException e ) { } catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e); throw new RuntimeException("Error cloning", e);
} }
} }
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
this.emitter = cloner.clone(emitter); this.emitter = cloner.clone(emitter);
} }
/** /**
* recursive function responsible for finding the newly cloned Emitter * recursive function responsible for finding the newly cloned Emitter
* *

@ -730,6 +730,8 @@ public class AudioNode extends Node implements AudioSource {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.direction = cloner.clone(direction); this.direction = cloner.clone(direction);
this.velocity = cloner.clone(velocity); this.velocity = cloner.clone(velocity);

@ -174,6 +174,13 @@ public class ParticleEmitter extends Geometry {
@Override @Override
public ParticleEmitter clone(boolean cloneMaterial) { public ParticleEmitter clone(boolean cloneMaterial) {
return (ParticleEmitter)super.clone(cloneMaterial);
}
/**
* The old clone() method that did not use the new Cloner utility.
*/
public ParticleEmitter oldClone(boolean cloneMaterial) {
ParticleEmitter clone = (ParticleEmitter) super.clone(cloneMaterial); ParticleEmitter clone = (ParticleEmitter) super.clone(cloneMaterial);
clone.shape = shape.deepClone(); clone.shape = shape.deepClone();
@ -216,6 +223,8 @@ public class ParticleEmitter extends Geometry {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.shape = cloner.clone(shape); this.shape = cloner.clone(shape);
this.control = cloner.clone(control); this.control = cloner.clone(control);
this.faceNormal = cloner.clone(faceNormal); this.faceNormal = cloner.clone(faceNormal);

@ -125,6 +125,8 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
// Change in behavior: the old origin was not cloned -pspeed // Change in behavior: the old origin was not cloned -pspeed
this.origin = cloner.clone(origin); this.origin = cloner.clone(origin);
} }

@ -90,6 +90,8 @@ public class BitmapText extends Node {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
for( int i = 0; i < textPages.length; i++ ) { for( int i = 0; i < textPages.length; i++ ) {
textPages[i] = cloner.clone(textPages[i]); textPages[i] = cloner.clone(textPages[i]);
} }

@ -76,6 +76,8 @@ public class AssetLinkNode extends Node {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
// This is a change in behavior because the old version did not clone // This is a change in behavior because the old version did not clone
// this list... changes to one clone would be reflected in all. // this list... changes to one clone would be reflected in all.
// I think that's probably undesirable. -pspeed // I think that's probably undesirable. -pspeed

@ -727,6 +727,8 @@ public class BatchNode extends GeometryGroupNode {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.batches = cloner.clone(batches); this.batches = cloner.clone(batches);
this.tmpFloat = cloner.clone(tmpFloat); this.tmpFloat = cloner.clone(tmpFloat);
this.tmpFloatN = cloner.clone(tmpFloatN); this.tmpFloatN = cloner.clone(tmpFloatN);

@ -100,6 +100,8 @@ public class CameraNode extends Node {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
// A change in behavior... I think previously CameraNode was probably // A change in behavior... I think previously CameraNode was probably
// not really cloneable... or at least its camControl would be pointing // not really cloneable... or at least its camControl would be pointing
// to the wrong control. -pspeed // to the wrong control. -pspeed

@ -44,6 +44,7 @@ import com.jme3.math.Matrix4f;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.clone.Cloner; import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.IdentityCloneFunction;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
import java.io.IOException; import java.io.IOException;
import java.util.Queue; import java.util.Queue;
@ -492,6 +493,13 @@ public class Geometry extends Spatial {
*/ */
@Override @Override
public Geometry clone(boolean cloneMaterial) { public Geometry clone(boolean cloneMaterial) {
return (Geometry)super.clone(cloneMaterial);
}
/**
* The old clone() method that did not use the new Cloner utility.
*/
public Geometry oldClone(boolean cloneMaterial) {
Geometry geomClone = (Geometry) super.clone(cloneMaterial); Geometry geomClone = (Geometry) super.clone(cloneMaterial);
// This geometry is managed, // This geometry is managed,
@ -535,6 +543,10 @@ public class Geometry extends Spatial {
*/ */
@Override @Override
public Spatial deepClone() { public Spatial deepClone() {
return super.deepClone();
}
public Spatial oldDeepClone() {
Geometry geomClone = clone(true); Geometry geomClone = clone(true);
geomClone.mesh = mesh.deepClone(); geomClone.mesh = mesh.deepClone();
return geomClone; return geomClone;
@ -545,9 +557,42 @@ public class Geometry extends Spatial {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
this.mesh = cloner.clone(mesh); super.cloneFields(cloner, original);
// If this is a grouped node and if our group node is
// also cloned then we'll grab it's reference.
if( groupNode != null ) {
if( cloner.isCloned(groupNode) ) {
// Then resolve the reference
this.groupNode = cloner.clone(groupNode);
} else {
// We are on our own now
this.groupNode = null;
this.startIndex = -1;
}
// The above is based on the fact that if we were
// cloning the hierarchy that contained the parent
// group then it would have been shallow cloned before
// this child. Can't really be otherwise.
}
this.cachedWorldMat = cloner.clone(cachedWorldMat);
// See if we are doing a shallow clone or a deep mesh clone
boolean shallowClone = (cloner.getCloneFunction(Mesh.class) instanceof IdentityCloneFunction);
// See if we clone the mesh using the special animation
// semi-deep cloning
if( shallowClone && mesh != null && mesh.getBuffer(Type.BindPosePosition) != null ) {
// Then we need to clone the mesh a little deeper
this.mesh = mesh.cloneForAnim();
} else {
// Do whatever the cloner wants to do about it
this.mesh = cloner.clone(mesh);
}
this.material = cloner.clone(material); this.material = cloner.clone(material);
this.groupNode = cloner.clone(groupNode);
} }
@Override @Override

@ -100,6 +100,8 @@ public class LightNode extends Node {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
// A change in behavior... I think previously LightNode was probably // A change in behavior... I think previously LightNode was probably
// not really cloneable... or at least its lightControl would be pointing // not really cloneable... or at least its lightControl would be pointing
// to the wrong control. -pspeed // to the wrong control. -pspeed

@ -307,7 +307,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
@Override @Override
public Mesh jmeClone() { public Mesh jmeClone() {
try { try {
return (Mesh)super.clone(); Mesh clone = (Mesh)super.clone();
clone.vertexArrayID = -1;
return clone;
} catch (CloneNotSupportedException ex) { } catch (CloneNotSupportedException ex) {
throw new AssertionError(); throw new AssertionError();
} }

@ -697,7 +697,17 @@ public class Node extends Spatial {
} }
@Override @Override
public Spatial deepClone(){ public Spatial deepClone() {
Node nodeClone = (Node)super.deepClone();
// Reset the fields of the clone that should be in a 'new' state.
nodeClone.updateList = null;
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
return nodeClone;
}
public Spatial oldDeepClone(){
Node nodeClone = (Node) super.clone(); Node nodeClone = (Node) super.clone();
nodeClone.children = new SafeArrayList<Spatial>(Spatial.class); nodeClone.children = new SafeArrayList<Spatial>(Spatial.class);
for (Spatial child : children){ for (Spatial child : children){
@ -713,6 +723,8 @@ public class Node extends Spatial {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.children = cloner.clone(children); this.children = cloner.clone(children);
// Only the outer cloning thing knows whether this should be nulled // Only the outer cloning thing knows whether this should be nulled

@ -48,6 +48,7 @@ import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner; import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.IdentityCloneFunction;
import com.jme3.util.clone.JmeCloneable; import com.jme3.util.clone.JmeCloneable;
import com.jme3.util.SafeArrayList; import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars; import com.jme3.util.TempVars;
@ -1263,12 +1264,42 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* Note that meshes of geometries are not cloned explicitly, they * Note that meshes of geometries are not cloned explicitly, they
* are shared if static, or specially cloned if animated. * are shared if static, or specially cloned if animated.
* *
* All controls will be cloned using the Control.cloneForSpatial method
* on the clone.
*
* @see Mesh#cloneForAnim() * @see Mesh#cloneForAnim()
*/ */
public Spatial clone(boolean cloneMaterial) { public Spatial clone( boolean cloneMaterial ) {
// Setup the cloner for the type of cloning we want to do.
Cloner cloner = new Cloner();
// First, we definitely do not want to clone our own parent
cloner.setClonedValue(parent, null);
// If we aren't cloning materials then we will make sure those
// aren't cloned also
if( !cloneMaterial ) {
cloner.setCloneFunction(Material.class, new IdentityCloneFunction<Material>());
}
// By default the meshes are not cloned. The geometry
// may choose to selectively force them to be cloned but
// normally they will be shared
cloner.setCloneFunction(Mesh.class, new IdentityCloneFunction<Mesh>());
// Clone it!
Spatial clone = cloner.clone(this);
// Because we've nulled the parent out we need to make sure
// the transforms and stuff get refreshed.
clone.setTransformRefresh();
clone.setLightListRefresh();
return clone;
}
/**
* The old clone() method that did not use the new Cloner utility.
*/
public Spatial oldClone(boolean cloneMaterial) {
try { try {
Spatial clone = (Spatial) super.clone(); Spatial clone = (Spatial) super.clone();
if (worldBound != null) { if (worldBound != null) {
@ -1344,7 +1375,22 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* *
* @see Spatial#clone() * @see Spatial#clone()
*/ */
public abstract Spatial deepClone(); public Spatial deepClone() {
// Setup the cloner for the type of cloning we want to do.
Cloner cloner = new Cloner();
// First, we definitely do not want to clone our own parent
cloner.setClonedValue(parent, null);
Spatial clone = cloner.clone(this);
// Because we've nulled the parent out we need to make sure
// the transforms and stuff get refreshed.
clone.setTransformRefresh();
clone.setLightListRefresh();
return clone;
}
/** /**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly. * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
@ -1381,13 +1427,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
// to avoid all of the nasty cloneForSpatial() fixup style code that // to avoid all of the nasty cloneForSpatial() fixup style code that
// used to inject stuff into the clone's user data. By using cloner // used to inject stuff into the clone's user data. By using cloner
// to clone the user data we get this automatically. // to clone the user data we get this automatically.
userData = (HashMap<String, Savable>)userData.clone(); if( userData != null ) {
for( Map.Entry<String, Savable> e : userData.entrySet() ) { userData = (HashMap<String, Savable>)userData.clone();
Savable value = e.getValue(); for( Map.Entry<String, Savable> e : userData.entrySet() ) {
if( value instanceof Cloneable ) { Savable value = e.getValue();
// Note: all JmeCloneable objects are also Cloneable so this if( value instanceof Cloneable ) {
// catches both cases. // Note: all JmeCloneable objects are also Cloneable so this
e.setValue(cloner.clone(value)); // catches both cases.
e.setValue(cloner.clone(value));
}
} }
} }
} }

@ -349,6 +349,8 @@ public class InstancedGeometry extends Geometry {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.globalInstanceData = cloner.clone(globalInstanceData); this.globalInstanceData = cloner.clone(globalInstanceData);
this.transformInstanceData = cloner.clone(transformInstanceData); this.transformInstanceData = cloner.clone(transformInstanceData);
this.geometries = cloner.clone(geometries); this.geometries = cloner.clone(geometries);

@ -350,6 +350,8 @@ public class InstancedNode extends GeometryGroupNode {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.control = cloner.clone(control); this.control = cloner.clone(control);
this.lookUp = cloner.clone(lookUp); this.lookUp = cloner.clone(lookUp);

@ -84,6 +84,7 @@ public class NormalRecalcControl extends AbstractControl {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.terrain = cloner.clone(terrain); this.terrain = cloner.clone(terrain);
} }

@ -74,12 +74,12 @@ import java.util.logging.Logger;
* This control serializes, but it does not save the Camera reference. * This control serializes, but it does not save the Camera reference.
* This camera reference has to be manually added in when you load the * This camera reference has to be manually added in when you load the
* terrain to the scene! * terrain to the scene!
* *
* When the control or the terrain are removed from the scene, you should call * When the control or the terrain are removed from the scene, you should call
* TerrainLodControl.detachAndCleanUpControl() to remove any threads it created * TerrainLodControl.detachAndCleanUpControl() to remove any threads it created
* to handle the LOD processing. If you supply your own executor service, then * to handle the LOD processing. If you supply your own executor service, then
* you have to handle its thread termination yourself. * you have to handle its thread termination yourself.
* *
* @author Brent Owens * @author Brent Owens
*/ */
public class TerrainLodControl extends AbstractControl { public class TerrainLodControl extends AbstractControl {
@ -92,15 +92,15 @@ public class TerrainLodControl extends AbstractControl {
private HashMap<String,UpdatedTerrainPatch> updatedPatches; private HashMap<String,UpdatedTerrainPatch> updatedPatches;
private final Object updatePatchesLock = new Object(); private final Object updatePatchesLock = new Object();
protected List<Vector3f> lastCameraLocations; // used for LOD calc protected List<Vector3f> lastCameraLocations; // used for LOD calc
private AtomicBoolean lodCalcRunning = new AtomicBoolean(false); private AtomicBoolean lodCalcRunning = new AtomicBoolean(false);
private int lodOffCount = 0; private int lodOffCount = 0;
protected ExecutorService executor; protected ExecutorService executor;
protected Future<HashMap<String, UpdatedTerrainPatch>> indexer; protected Future<HashMap<String, UpdatedTerrainPatch>> indexer;
private boolean forceUpdate = true; private boolean forceUpdate = true;
public TerrainLodControl() { public TerrainLodControl() {
} }
@ -111,7 +111,7 @@ public class TerrainLodControl extends AbstractControl {
this.cameras = cams; this.cameras = cams;
lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator
} }
/** /**
* Only uses the first camera right now. * Only uses the first camera right now.
* @param terrain to act upon (must be a Spatial) * @param terrain to act upon (must be a Spatial)
@ -134,7 +134,7 @@ public class TerrainLodControl extends AbstractControl {
public void setExecutor(ExecutorService executor) { public void setExecutor(ExecutorService executor) {
this.executor = executor; this.executor = executor;
} }
protected ExecutorService createExecutorService() { protected ExecutorService createExecutorService() {
return Executors.newSingleThreadExecutor(new ThreadFactory() { return Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
@ -145,14 +145,14 @@ public class TerrainLodControl extends AbstractControl {
} }
}); });
} }
@Override @Override
protected void controlUpdate(float tpf) { protected void controlUpdate(float tpf) {
//list of cameras for when terrain supports multiple cameras (ie split screen) //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) {
// this will get run once // this will get run once
@ -160,7 +160,7 @@ public class TerrainLodControl extends AbstractControl {
lodCalculator.turnOffLod(); lodCalculator.turnOffLod();
} }
} }
if (cameras != null) { if (cameras != null) {
cameraLocations.clear(); cameraLocations.clear();
for (Camera c : cameras) // populate them for (Camera c : cameras) // populate them
@ -170,7 +170,7 @@ public class TerrainLodControl extends AbstractControl {
updateLOD(cameraLocations, lodCalculator); updateLOD(cameraLocations, lodCalculator);
} }
} }
/** /**
* Call this when you remove the terrain or this control from the scene. * Call this when you remove the terrain or this control from the scene.
* It will clear up any threads it had. * It will clear up any threads it had.
@ -186,7 +186,7 @@ public class TerrainLodControl extends AbstractControl {
if(getSpatial() == null){ if(getSpatial() == null){
return; return;
} }
// update any existing ones that need updating // update any existing ones that need updating
updateQuadLODs(); updateQuadLODs();
@ -196,9 +196,9 @@ public class TerrainLodControl extends AbstractControl {
return; return;
else else
lodOffCount++; lodOffCount++;
} else } else
lodOffCount = 0; lodOffCount = 0;
if (lastCameraLocations != null) { if (lastCameraLocations != null) {
if (!forceUpdate && lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff()) if (!forceUpdate && lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff())
return; // don't update if in same spot return; // don't update if in same spot
@ -218,9 +218,9 @@ public class TerrainLodControl extends AbstractControl {
if (executor == null) if (executor == null)
executor = createExecutorService(); executor = createExecutorService();
prepareTerrain(); prepareTerrain();
UpdateLOD updateLodThread = getLodThread(locations, lodCalculator); UpdateLOD updateLodThread = getLodThread(locations, lodCalculator);
indexer = executor.submit(updateLodThread); indexer = executor.submit(updateLodThread);
} }
@ -232,12 +232,12 @@ public class TerrainLodControl extends AbstractControl {
public void forceUpdate() { public void forceUpdate() {
this.forceUpdate = true; this.forceUpdate = true;
} }
protected void prepareTerrain() { protected void prepareTerrain() {
TerrainQuad terrain = (TerrainQuad)getSpatial(); TerrainQuad terrain = (TerrainQuad)getSpatial();
terrain.cacheTerrainTransforms();// cache the terrain's world transforms so they can be accessed on the separate thread safely terrain.cacheTerrainTransforms();// cache the terrain's world transforms so they can be accessed on the separate thread safely
} }
protected UpdateLOD getLodThread(List<Vector3f> locations, LodCalculator lodCalculator) { protected UpdateLOD getLodThread(List<Vector3f> locations, LodCalculator lodCalculator) {
return new UpdateLOD(locations, lodCalculator); return new UpdateLOD(locations, lodCalculator);
} }
@ -249,7 +249,7 @@ public class TerrainLodControl extends AbstractControl {
if (indexer != null) { if (indexer != null) {
if (indexer.isDone()) { if (indexer.isDone()) {
try { try {
HashMap<String, UpdatedTerrainPatch> updated = indexer.get(); HashMap<String, UpdatedTerrainPatch> updated = indexer.get();
if (updated != null) { if (updated != null) {
// do the actual geometry update here // do the actual geometry update here
@ -257,7 +257,7 @@ public class TerrainLodControl extends AbstractControl {
utp.updateAll(); utp.updateAll();
} }
} }
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) { } catch (ExecutionException ex) {
@ -268,7 +268,7 @@ public class TerrainLodControl extends AbstractControl {
} }
} }
} }
private boolean lastCameraLocationsTheSame(List<Vector3f> locations) { private boolean lastCameraLocationsTheSame(List<Vector3f> locations) {
boolean theSame = true; boolean theSame = true;
for (Vector3f l : locations) { for (Vector3f l : locations) {
@ -281,7 +281,7 @@ public class TerrainLodControl extends AbstractControl {
} }
return theSame; return theSame;
} }
protected synchronized boolean isLodCalcRunning() { protected synchronized boolean isLodCalcRunning() {
return lodCalcRunning.get(); return lodCalcRunning.get();
} }
@ -297,11 +297,11 @@ public class TerrainLodControl extends AbstractControl {
return cloned; return cloned;
} }
@Override @Override
public Object jmeClone() { public Object jmeClone() {
if (spatial instanceof Terrain) { if (spatial instanceof Terrain) {
TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, cameras); TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, cameras);
@ -310,21 +310,23 @@ public class TerrainLodControl extends AbstractControl {
return cloned; return cloned;
} }
return null; return null;
} }
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.lodCalculator = cloner.clone(lodCalculator); this.lodCalculator = cloner.clone(lodCalculator);
try { try {
// Not deep clone of the cameras themselves // Not deep clone of the cameras themselves
this.cameras = cloner.javaClone(cameras); this.cameras = cloner.javaClone(cameras);
} catch( CloneNotSupportedException e ) { } catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning", e); throw new RuntimeException("Error cloning", e);
} }
} }
@Override @Override
public Control cloneForSpatial(Spatial spatial) { public Control cloneForSpatial(Spatial spatial) {
if (spatial instanceof Terrain) { if (spatial instanceof Terrain) {
@ -346,7 +348,7 @@ public class TerrainLodControl extends AbstractControl {
cams.add(camera); cams.add(camera);
setCameras(cams); setCameras(cams);
} }
public void setCameras(List<Camera> cameras) { public void setCameras(List<Camera> cameras) {
this.cameras = cameras; this.cameras = cameras;
cameraLocations.clear(); cameraLocations.clear();
@ -374,7 +376,7 @@ public class TerrainLodControl extends AbstractControl {
public void setLodCalculator(LodCalculator lodCalculator) { public void setLodCalculator(LodCalculator lodCalculator) {
this.lodCalculator = lodCalculator; this.lodCalculator = lodCalculator;
} }
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
this.enabled = enabled; this.enabled = enabled;
@ -386,8 +388,8 @@ public class TerrainLodControl extends AbstractControl {
lodCalculator.turnOnLod(); lodCalculator.turnOnLod();
} }
} }
/** /**
* Calculates the LOD of all child terrain patches. * Calculates the LOD of all child terrain patches.
*/ */
@ -408,7 +410,7 @@ public class TerrainLodControl extends AbstractControl {
setLodCalcRunning(true); 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>(); HashMap<String,UpdatedTerrainPatch> updated = new HashMap<String,UpdatedTerrainPatch>();
boolean lodChanged = terrainQuad.calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here boolean lodChanged = terrainQuad.calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here
@ -418,8 +420,8 @@ public class TerrainLodControl extends AbstractControl {
setLodCalcRunning(false); setLodCalcRunning(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);
@ -430,7 +432,7 @@ public class TerrainLodControl extends AbstractControl {
//setUpdateQuadLODs(updated); // set back to main ogl thread //setUpdateQuadLODs(updated); // set back to main ogl thread
setLodCalcRunning(false); setLodCalcRunning(false);
return updated; return updated;
} }
} }

@ -961,6 +961,7 @@ public class TerrainPatch extends Geometry {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.stepScale = cloner.clone(stepScale); this.stepScale = cloner.clone(stepScale);
this.offset = cloner.clone(offset); this.offset = cloner.clone(offset);

@ -1813,6 +1813,8 @@ public class TerrainQuad extends Node implements Terrain {
*/ */
@Override @Override
public void cloneFields( Cloner cloner, Object original ) { public void cloneFields( Cloner cloner, Object original ) {
super.cloneFields(cloner, original);
this.stepScale = cloner.clone(stepScale); this.stepScale = cloner.clone(stepScale);
this.offset = cloner.clone(offset); this.offset = cloner.clone(offset);

Loading…
Cancel
Save