diff --git a/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java b/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java index 82af6e135..89500c1cd 100644 --- a/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java +++ b/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java @@ -118,22 +118,17 @@ public class EffectTrack implements ClonableTrack { } } - @Override + @Override public Object jmeClone() { KillParticleControl c = new KillParticleControl(); //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, - //making it remove itself from the spatial in the first update loop when loaded was the less bad. + //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. c.remove = true; c.spatial = spatial; return c; - } - - @Override - public void cloneFields( Cloner cloner, Object original ) { - this.spatial = cloner.clone(spatial); } - + @Override protected void controlRender(RenderManager rm, ViewPort vp) { } @@ -143,8 +138,8 @@ public class EffectTrack implements ClonableTrack { KillParticleControl c = new KillParticleControl(); //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, - //making it remove itself from the spatial in the first update loop when loaded was the less bad. + //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. c.remove = true; c.setSpatial(spatial); return c; @@ -261,7 +256,7 @@ public class EffectTrack implements ClonableTrack { public float[] getKeyFrameTimes() { return new float[] { startOffset }; } - + /** * Clone this track * @@ -302,21 +297,21 @@ public class EffectTrack implements ClonableTrack { return effectTrack; } - @Override + @Override public Object jmeClone() { try { return super.clone(); } catch( CloneNotSupportedException e ) { throw new RuntimeException("Error cloning", e); } - } + } - @Override - public void cloneFields( Cloner cloner, Object original ) { + @Override + public void cloneFields( Cloner cloner, Object original ) { this.emitter = cloner.clone(emitter); } - + /** * recursive function responsible for finding the newly cloned Emitter * diff --git a/jme3-core/src/main/java/com/jme3/audio/AudioNode.java b/jme3-core/src/main/java/com/jme3/audio/AudioNode.java index bf4705d87..55a57768d 100644 --- a/jme3-core/src/main/java/com/jme3/audio/AudioNode.java +++ b/jme3-core/src/main/java/com/jme3/audio/AudioNode.java @@ -730,6 +730,8 @@ public class AudioNode extends Node implements AudioSource { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.direction = cloner.clone(direction); this.velocity = cloner.clone(velocity); diff --git a/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java b/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java index 3baf8feb6..1af2bf1d1 100644 --- a/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java +++ b/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java @@ -174,6 +174,13 @@ public class ParticleEmitter extends Geometry { @Override 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); clone.shape = shape.deepClone(); @@ -216,6 +223,8 @@ public class ParticleEmitter extends Geometry { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.shape = cloner.clone(shape); this.control = cloner.clone(control); this.faceNormal = cloner.clone(faceNormal); diff --git a/jme3-core/src/main/java/com/jme3/effect/influencers/RadialParticleInfluencer.java b/jme3-core/src/main/java/com/jme3/effect/influencers/RadialParticleInfluencer.java index 0cacfe5b0..fba223dc1 100644 --- a/jme3-core/src/main/java/com/jme3/effect/influencers/RadialParticleInfluencer.java +++ b/jme3-core/src/main/java/com/jme3/effect/influencers/RadialParticleInfluencer.java @@ -125,6 +125,8 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + // Change in behavior: the old origin was not cloned -pspeed this.origin = cloner.clone(origin); } diff --git a/jme3-core/src/main/java/com/jme3/font/BitmapText.java b/jme3-core/src/main/java/com/jme3/font/BitmapText.java index 20f5cec3b..4dfd87aaa 100644 --- a/jme3-core/src/main/java/com/jme3/font/BitmapText.java +++ b/jme3-core/src/main/java/com/jme3/font/BitmapText.java @@ -90,6 +90,8 @@ public class BitmapText extends Node { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + for( int i = 0; i < textPages.length; i++ ) { textPages[i] = cloner.clone(textPages[i]); } diff --git a/jme3-core/src/main/java/com/jme3/scene/AssetLinkNode.java b/jme3-core/src/main/java/com/jme3/scene/AssetLinkNode.java index fd6900a97..d31997f5b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/AssetLinkNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/AssetLinkNode.java @@ -76,6 +76,8 @@ public class AssetLinkNode extends Node { */ @Override 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 list... changes to one clone would be reflected in all. // I think that's probably undesirable. -pspeed diff --git a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java index 1338b50ef..1b0d5e051 100644 --- a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java @@ -727,6 +727,8 @@ public class BatchNode extends GeometryGroupNode { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.batches = cloner.clone(batches); this.tmpFloat = cloner.clone(tmpFloat); this.tmpFloatN = cloner.clone(tmpFloatN); diff --git a/jme3-core/src/main/java/com/jme3/scene/CameraNode.java b/jme3-core/src/main/java/com/jme3/scene/CameraNode.java index 11de0c3c0..44fed8208 100644 --- a/jme3-core/src/main/java/com/jme3/scene/CameraNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/CameraNode.java @@ -100,6 +100,8 @@ public class CameraNode extends Node { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + // A change in behavior... I think previously CameraNode was probably // not really cloneable... or at least its camControl would be pointing // to the wrong control. -pspeed diff --git a/jme3-core/src/main/java/com/jme3/scene/Geometry.java b/jme3-core/src/main/java/com/jme3/scene/Geometry.java index 6cd8f4758..f2f33501a 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Geometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/Geometry.java @@ -44,6 +44,7 @@ import com.jme3.math.Matrix4f; import com.jme3.renderer.Camera; import com.jme3.scene.VertexBuffer.Type; import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.IdentityCloneFunction; import com.jme3.util.TempVars; import java.io.IOException; import java.util.Queue; @@ -492,6 +493,13 @@ public class Geometry extends Spatial { */ @Override 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); // This geometry is managed, @@ -535,6 +543,10 @@ public class Geometry extends Spatial { */ @Override public Spatial deepClone() { + return super.deepClone(); + } + + public Spatial oldDeepClone() { Geometry geomClone = clone(true); geomClone.mesh = mesh.deepClone(); return geomClone; @@ -545,9 +557,42 @@ public class Geometry extends Spatial { */ @Override 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.groupNode = cloner.clone(groupNode); } @Override diff --git a/jme3-core/src/main/java/com/jme3/scene/LightNode.java b/jme3-core/src/main/java/com/jme3/scene/LightNode.java index 1a87d11b4..a64250c50 100644 --- a/jme3-core/src/main/java/com/jme3/scene/LightNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/LightNode.java @@ -100,6 +100,8 @@ public class LightNode extends Node { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + // A change in behavior... I think previously LightNode was probably // not really cloneable... or at least its lightControl would be pointing // to the wrong control. -pspeed diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index f9b66bc45..6a0b41c8c 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -307,7 +307,9 @@ public class Mesh implements Savable, Cloneable, JmeCloneable { @Override public Mesh jmeClone() { try { - return (Mesh)super.clone(); + Mesh clone = (Mesh)super.clone(); + clone.vertexArrayID = -1; + return clone; } catch (CloneNotSupportedException ex) { throw new AssertionError(); } diff --git a/jme3-core/src/main/java/com/jme3/scene/Node.java b/jme3-core/src/main/java/com/jme3/scene/Node.java index 59f0beb85..35526c3ce 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Node.java +++ b/jme3-core/src/main/java/com/jme3/scene/Node.java @@ -697,7 +697,17 @@ public class Node extends Spatial { } @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(); nodeClone.children = new SafeArrayList(Spatial.class); for (Spatial child : children){ @@ -713,6 +723,8 @@ public class Node extends Spatial { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.children = cloner.clone(children); // Only the outer cloning thing knows whether this should be nulled diff --git a/jme3-core/src/main/java/com/jme3/scene/Spatial.java b/jme3-core/src/main/java/com/jme3/scene/Spatial.java index f833b6758..15757eee0 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Spatial.java +++ b/jme3-core/src/main/java/com/jme3/scene/Spatial.java @@ -48,6 +48,7 @@ import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.control.Control; import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.IdentityCloneFunction; import com.jme3.util.clone.JmeCloneable; import com.jme3.util.SafeArrayList; 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 * 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() */ - 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()); + } + + // 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()); + + // 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 { Spatial clone = (Spatial) super.clone(); if (worldBound != null) { @@ -1344,7 +1375,22 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab * * @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. @@ -1381,13 +1427,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab // to avoid all of the nasty cloneForSpatial() fixup style code that // used to inject stuff into the clone's user data. By using cloner // to clone the user data we get this automatically. - userData = (HashMap)userData.clone(); - for( Map.Entry e : userData.entrySet() ) { - Savable value = e.getValue(); - if( value instanceof Cloneable ) { - // Note: all JmeCloneable objects are also Cloneable so this - // catches both cases. - e.setValue(cloner.clone(value)); + if( userData != null ) { + userData = (HashMap)userData.clone(); + for( Map.Entry e : userData.entrySet() ) { + Savable value = e.getValue(); + if( value instanceof Cloneable ) { + // Note: all JmeCloneable objects are also Cloneable so this + // catches both cases. + e.setValue(cloner.clone(value)); + } } } } diff --git a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java index b57345664..c0bc90e3d 100644 --- a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java @@ -349,6 +349,8 @@ public class InstancedGeometry extends Geometry { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.globalInstanceData = cloner.clone(globalInstanceData); this.transformInstanceData = cloner.clone(transformInstanceData); this.geometries = cloner.clone(geometries); diff --git a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java index c3cdd21e3..42f8a7615 100644 --- a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java @@ -350,6 +350,8 @@ public class InstancedNode extends GeometryGroupNode { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.control = cloner.clone(control); this.lookUp = cloner.clone(lookUp); diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/NormalRecalcControl.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/NormalRecalcControl.java index f810fbf4d..f6682dbe2 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/NormalRecalcControl.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/NormalRecalcControl.java @@ -84,6 +84,7 @@ public class NormalRecalcControl extends AbstractControl { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); this.terrain = cloner.clone(terrain); } diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java index f52b1ca89..d551ff4e5 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainLodControl.java @@ -74,12 +74,12 @@ import java.util.logging.Logger; * This control serializes, but it does not save the Camera reference. * This camera reference has to be manually added in when you load the * terrain to the scene! - * + * * When the control or the terrain are removed from the scene, you should call * TerrainLodControl.detachAndCleanUpControl() to remove any threads it created * to handle the LOD processing. If you supply your own executor service, then * you have to handle its thread termination yourself. - * + * * @author Brent Owens */ public class TerrainLodControl extends AbstractControl { @@ -92,15 +92,15 @@ public class TerrainLodControl extends AbstractControl { private HashMap updatedPatches; private final Object updatePatchesLock = new Object(); - + protected List lastCameraLocations; // used for LOD calc private AtomicBoolean lodCalcRunning = new AtomicBoolean(false); private int lodOffCount = 0; - + protected ExecutorService executor; protected Future> indexer; private boolean forceUpdate = true; - + public TerrainLodControl() { } @@ -111,7 +111,7 @@ public class TerrainLodControl extends AbstractControl { this.cameras = cams; lodCalculator = new DistanceLodCalculator(65, 2.7f); // a default calculator } - + /** * Only uses the first camera right now. * @param terrain to act upon (must be a Spatial) @@ -134,7 +134,7 @@ public class TerrainLodControl extends AbstractControl { public void setExecutor(ExecutorService executor) { this.executor = executor; } - + protected ExecutorService createExecutorService() { return Executors.newSingleThreadExecutor(new ThreadFactory() { public Thread newThread(Runnable r) { @@ -145,14 +145,14 @@ public class TerrainLodControl extends AbstractControl { } }); } - + @Override protected void controlUpdate(float tpf) { //list of cameras for when terrain supports multiple cameras (ie split screen) if (lodCalculator == null) return; - + if (!enabled) { if (!hasResetLod) { // this will get run once @@ -160,7 +160,7 @@ public class TerrainLodControl extends AbstractControl { lodCalculator.turnOffLod(); } } - + if (cameras != null) { cameraLocations.clear(); for (Camera c : cameras) // populate them @@ -170,7 +170,7 @@ public class TerrainLodControl extends AbstractControl { updateLOD(cameraLocations, lodCalculator); } } - + /** * Call this when you remove the terrain or this control from the scene. * It will clear up any threads it had. @@ -186,7 +186,7 @@ public class TerrainLodControl extends AbstractControl { if(getSpatial() == null){ return; } - + // update any existing ones that need updating updateQuadLODs(); @@ -196,9 +196,9 @@ public class TerrainLodControl extends AbstractControl { return; else lodOffCount++; - } else + } else lodOffCount = 0; - + if (lastCameraLocations != null) { if (!forceUpdate && lastCameraLocationsTheSame(locations) && !lodCalculator.isLodOff()) return; // don't update if in same spot @@ -218,9 +218,9 @@ public class TerrainLodControl extends AbstractControl { if (executor == null) executor = createExecutorService(); - + prepareTerrain(); - + UpdateLOD updateLodThread = getLodThread(locations, lodCalculator); indexer = executor.submit(updateLodThread); } @@ -232,12 +232,12 @@ public class TerrainLodControl extends AbstractControl { public void forceUpdate() { this.forceUpdate = true; } - + 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 } - + protected UpdateLOD getLodThread(List locations, LodCalculator lodCalculator) { return new UpdateLOD(locations, lodCalculator); } @@ -249,7 +249,7 @@ public class TerrainLodControl extends AbstractControl { if (indexer != null) { if (indexer.isDone()) { try { - + HashMap updated = indexer.get(); if (updated != null) { // do the actual geometry update here @@ -257,7 +257,7 @@ public class TerrainLodControl extends AbstractControl { utp.updateAll(); } } - + } catch (InterruptedException ex) { Logger.getLogger(TerrainLodControl.class.getName()).log(Level.SEVERE, null, ex); } catch (ExecutionException ex) { @@ -268,7 +268,7 @@ public class TerrainLodControl extends AbstractControl { } } } - + private boolean lastCameraLocationsTheSame(List locations) { boolean theSame = true; for (Vector3f l : locations) { @@ -281,7 +281,7 @@ public class TerrainLodControl extends AbstractControl { } return theSame; } - + protected synchronized boolean isLodCalcRunning() { return lodCalcRunning.get(); } @@ -297,11 +297,11 @@ public class TerrainLodControl extends AbstractControl { return cloned; } - - - - - @Override + + + + + @Override public Object jmeClone() { if (spatial instanceof Terrain) { TerrainLodControl cloned = new TerrainLodControl((Terrain) spatial, cameras); @@ -310,21 +310,23 @@ public class TerrainLodControl extends AbstractControl { return cloned; } return null; - } + } - @Override + @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.lodCalculator = cloner.clone(lodCalculator); - - try { + + try { // Not deep clone of the cameras themselves this.cameras = cloner.javaClone(cameras); } catch( CloneNotSupportedException e ) { throw new RuntimeException("Error cloning", e); - } - } - - + } + } + + @Override public Control cloneForSpatial(Spatial spatial) { if (spatial instanceof Terrain) { @@ -346,7 +348,7 @@ public class TerrainLodControl extends AbstractControl { cams.add(camera); setCameras(cams); } - + public void setCameras(List cameras) { this.cameras = cameras; cameraLocations.clear(); @@ -374,7 +376,7 @@ public class TerrainLodControl extends AbstractControl { public void setLodCalculator(LodCalculator lodCalculator) { this.lodCalculator = lodCalculator; } - + @Override public void setEnabled(boolean enabled) { this.enabled = enabled; @@ -386,8 +388,8 @@ public class TerrainLodControl extends AbstractControl { lodCalculator.turnOnLod(); } } - - + + /** * Calculates the LOD of all child terrain patches. */ @@ -408,7 +410,7 @@ public class TerrainLodControl extends AbstractControl { setLodCalcRunning(true); TerrainQuad terrainQuad = (TerrainQuad)getSpatial(); - + // go through each patch and calculate its LOD based on camera distance HashMap updated = new HashMap(); boolean lodChanged = terrainQuad.calculateLod(camLocations, updated, lodCalculator); // 'updated' gets populated here @@ -418,8 +420,8 @@ public class TerrainLodControl extends AbstractControl { setLodCalcRunning(false); return null; } - - + + // then calculate its neighbour LOD values for seaming in the shader terrainQuad.findNeighboursLod(updated); @@ -430,7 +432,7 @@ public class TerrainLodControl extends AbstractControl { //setUpdateQuadLODs(updated); // set back to main ogl thread setLodCalcRunning(false); - + return updated; } } diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java index 0f6a1bb8d..4641f4de6 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java @@ -961,6 +961,7 @@ public class TerrainPatch extends Geometry { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); this.stepScale = cloner.clone(stepScale); this.offset = cloner.clone(offset); diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java index 2553e06a0..e21b89155 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainQuad.java @@ -1813,6 +1813,8 @@ public class TerrainQuad extends Node implements Terrain { */ @Override public void cloneFields( Cloner cloner, Object original ) { + super.cloneFields(cloner, original); + this.stepScale = cloner.clone(stepScale); this.offset = cloner.clone(offset);