From 7628b0f9e34447d81ab9f21f2467762b3675350f Mon Sep 17 00:00:00 2001
From: Fadorico <>
Date: Wed, 9 Mar 2016 15:08:56 -0500
Subject: [PATCH 01/77] Fixed collision group listeners not being notified
---
.../src/main/java/com/jme3/bullet/PhysicsSpace.java | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
index edea485ef..51fa0fcea 100644
--- a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
+++ b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
@@ -334,6 +334,19 @@ public class PhysicsSpace {
private void addCollisionEvent_native(PhysicsCollisionObject node, PhysicsCollisionObject node1, long manifoldPointObjectId) {
// System.out.println("addCollisionEvent:"+node.getObjectId()+" "+ node1.getObjectId());
collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, manifoldPointObjectId));
+
+ // Notify group listeners
+ if((node.getCollideWithGroups() & node1.getCollisionGroup()) > 0
+ || (node1.getCollideWithGroups() & node.getCollisionGroup()) > 0){
+ PhysicsCollisionGroupListener listener = collisionGroupListeners.get(node.getCollisionGroup());
+ PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(node1.getCollisionGroup());
+ if(listener != null){
+ listener.collide(node, node1);
+ }
+ if(listener1 != null && node.getCollisionGroup() != node1.getCollisionGroup()){
+ listener1.collide(node, node1);
+ }
+ }
}
/**
From 48b3f1a4d3e843e72de46495b10de7eb699fd3db Mon Sep 17 00:00:00 2001
From: jmekaelthas
Date: Tue, 15 Mar 2016 17:18:16 +0100
Subject: [PATCH 02/77] Bugfix: fixes to face triangulation and some edges
computations.
---
.../jme3/scene/plugins/blender/meshes/Edge.java | 15 ++++++++++-----
.../jme3/scene/plugins/blender/meshes/Face.java | 10 ----------
2 files changed, 10 insertions(+), 15 deletions(-)
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java
index 1d76fc02f..12aff2f4f 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java
+++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java
@@ -215,8 +215,8 @@ public class Edge {
/**
* The method computes the crossing pint of this edge and another edge. If
- * there is no crossing then null is returned. This method also allows to
- * get the crossing point of the straight lines that contain these edges if
+ * there is no crossing then null is returned. Also null is returned if the edges are parallel.
+ * This method also allows to get the crossing point of the straight lines that contain these edges if
* you set the 'extend' parameter to true.
*
* @param edge
@@ -227,7 +227,7 @@ public class Edge {
* @param extendSecondEdge
* set to true to find a crossing point along the whole
* straight that contains the given edge
- * @return cross point on null if none exist
+ * @return cross point on null if none exist or the edges are parallel
*/
public Vector3f getCrossPoint(Edge edge, boolean extendThisEdge, boolean extendSecondEdge) {
Vector3d P1 = new Vector3d(this.getFirstVertex());
@@ -235,6 +235,11 @@ public class Edge {
Vector3d u = new Vector3d(this.getSecondVertex()).subtract(P1).normalizeLocal();
Vector3d v = new Vector3d(edge.getSecondVertex()).subtract(P2).normalizeLocal();
+ if(Math.abs(u.dot(v)) >= 1 - FastMath.DBL_EPSILON) {
+ // the edges are parallel; do not care about the crossing point
+ return null;
+ }
+
double t1 = 0, t2 = 0;
if(u.x == 0 && v.x == 0) {
t2 = (u.z * (P2.y - P1.y) - u.y * (P2.z - P1.z)) / (u.y * v.z - u.z * v.y);
@@ -262,11 +267,11 @@ public class Edge {
// the lines cross, check if p1 and p2 are within the edges
Vector3d p = p1.subtract(P1);
double cos = p.dot(u) / p.length();
- if (extendThisEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() <= this.getLength()) {
+ if (extendThisEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() - this.getLength() <= FastMath.FLT_EPSILON) {
// p1 is inside the first edge, lets check the other edge now
p = p2.subtract(P2);
cos = p.dot(v) / p.length();
- if(extendSecondEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() <= edge.getLength()) {
+ if(extendSecondEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() - edge.getLength() <= FastMath.FLT_EPSILON) {
return p1.toVector3f();
}
}
diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java
index a41d58ff0..a746df180 100644
--- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java
+++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java
@@ -279,16 +279,6 @@ public class Face implements Comparator {
// two special cases will improve the computations speed
if(face.getIndexes().size() == 3) {
triangulatedFaces.add(face.getIndexes().clone());
- } else if(face.getIndexes().size() == 4) {
- // in case face has 4 verts we use the plain triangulation
- indexes[0] = face.getIndex(0);
- indexes[1] = face.getIndex(1);
- indexes[2] = face.getIndex(2);
- triangulatedFaces.add(new IndexesLoop(indexes));
-
- indexes[1] = face.getIndex(2);
- indexes[2] = face.getIndex(3);
- triangulatedFaces.add(new IndexesLoop(indexes));
} else {
int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1;
while (face.vertexCount() > 0) {
From 15c9c083cd14b99e0460a2a8930701ff053dfac1 Mon Sep 17 00:00:00 2001
From: MeFisto94
Date: Sun, 20 Mar 2016 10:11:04 +0100
Subject: [PATCH 03/77] Allow MotionEvents to be cloned
---
.../main/java/com/jme3/cinematic/events/MotionEvent.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
index dbb9b494c..fc277880b 100644
--- a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
+++ b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2016 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -64,9 +64,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
protected int currentWayPoint;
protected float currentValue;
protected Vector3f direction = new Vector3f();
- protected Vector3f lookAt;
+ protected Vector3f lookAt = Vector3f.ZERO;
protected Vector3f upVector = Vector3f.UNIT_Y;
- protected Quaternion rotation;
+ protected Quaternion rotation = Quaternion.IDENTITY;
protected Direction directionType = Direction.None;
protected MotionPath path;
private boolean isControl = true;
From ef626b095958f1ca4dc536d0eda3f7c4a06270c1 Mon Sep 17 00:00:00 2001
From: Fadorico
Date: Wed, 23 Mar 2016 02:37:03 -0400
Subject: [PATCH 04/77] Fixed group collision check in native broadphase
---
.../src/native/cpp/jmeClasses.cpp | 2 ++
.../src/native/cpp/jmeClasses.h | 1 +
.../src/native/cpp/jmePhysicsSpace.cpp | 24 +++++++++++++++++--
.../java/com/jme3/bullet/PhysicsSpace.java | 23 +++++++++---------
4 files changed, 37 insertions(+), 13 deletions(-)
diff --git a/jme3-bullet-native/src/native/cpp/jmeClasses.cpp b/jme3-bullet-native/src/native/cpp/jmeClasses.cpp
index 6b7caa0e9..ae7784c82 100644
--- a/jme3-bullet-native/src/native/cpp/jmeClasses.cpp
+++ b/jme3-bullet-native/src/native/cpp/jmeClasses.cpp
@@ -40,6 +40,7 @@ jclass jmeClasses::PhysicsSpace;
jmethodID jmeClasses::PhysicsSpace_preTick;
jmethodID jmeClasses::PhysicsSpace_postTick;
jmethodID jmeClasses::PhysicsSpace_addCollisionEvent;
+jmethodID jmeClasses::PhysicsSpace_notifyCollisionGroupListeners;
jclass jmeClasses::PhysicsGhostObject;
jmethodID jmeClasses::PhysicsGhostObject_addOverlappingObject;
@@ -137,6 +138,7 @@ void jmeClasses::initJavaClasses(JNIEnv* env) {
PhysicsSpace_preTick = env->GetMethodID(PhysicsSpace, "preTick_native", "(F)V");
PhysicsSpace_postTick = env->GetMethodID(PhysicsSpace, "postTick_native", "(F)V");
PhysicsSpace_addCollisionEvent = env->GetMethodID(PhysicsSpace, "addCollisionEvent_native","(Lcom/jme3/bullet/collision/PhysicsCollisionObject;Lcom/jme3/bullet/collision/PhysicsCollisionObject;J)V");
+ PhysicsSpace_notifyCollisionGroupListeners = env->GetMethodID(PhysicsSpace, "notifyCollisionGroupListeners_native","(Lcom/jme3/bullet/collision/PhysicsCollisionObject;Lcom/jme3/bullet/collision/PhysicsCollisionObject;)Z");
if (env->ExceptionCheck()) {
env->Throw(env->ExceptionOccurred());
return;
diff --git a/jme3-bullet-native/src/native/cpp/jmeClasses.h b/jme3-bullet-native/src/native/cpp/jmeClasses.h
index bb1b0e99a..bdead6b70 100644
--- a/jme3-bullet-native/src/native/cpp/jmeClasses.h
+++ b/jme3-bullet-native/src/native/cpp/jmeClasses.h
@@ -46,6 +46,7 @@ public:
static jmethodID PhysicsSpace_addCollisionEvent;
static jclass PhysicsGhostObject;
static jmethodID PhysicsGhostObject_addOverlappingObject;
+ static jmethodID PhysicsSpace_notifyCollisionGroupListeners;
static jclass Vector3f;
static jmethodID Vector3f_set;
diff --git a/jme3-bullet-native/src/native/cpp/jmePhysicsSpace.cpp b/jme3-bullet-native/src/native/cpp/jmePhysicsSpace.cpp
index 34c77c407..8cb80bc87 100644
--- a/jme3-bullet-native/src/native/cpp/jmePhysicsSpace.cpp
+++ b/jme3-bullet-native/src/native/cpp/jmePhysicsSpace.cpp
@@ -187,8 +187,28 @@ void jmePhysicsSpace::createPhysicsSpace(jfloat minX, jfloat minY, jfloat minZ,
jmeUserPointer *up0 = (jmeUserPointer*) co0 -> getUserPointer();
jmeUserPointer *up1 = (jmeUserPointer*) co1 -> getUserPointer();
if (up0 != NULL && up1 != NULL) {
- collides = (up0->group & up1->groups) != 0;
- collides = collides && (up1->group & up0->groups);
+ collides = (up0->group & up1->groups) != 0 || (up1->group & up0->groups) != 0;
+
+ if(collides){
+ jmePhysicsSpace *dynamicsWorld = (jmePhysicsSpace *)up0->space;
+ JNIEnv* env = dynamicsWorld->getEnv();
+ jobject javaPhysicsSpace = env->NewLocalRef(dynamicsWorld->getJavaPhysicsSpace());
+ jobject javaCollisionObject0 = env->NewLocalRef(up0->javaCollisionObject);
+ jobject javaCollisionObject1 = env->NewLocalRef(up1->javaCollisionObject);
+
+ jboolean notifyResult = env->CallBooleanMethod(javaPhysicsSpace, jmeClasses::PhysicsSpace_notifyCollisionGroupListeners, javaCollisionObject0, javaCollisionObject1);
+
+ env->DeleteLocalRef(javaPhysicsSpace);
+ env->DeleteLocalRef(javaCollisionObject0);
+ env->DeleteLocalRef(javaCollisionObject1);
+
+ if (env->ExceptionCheck()) {
+ env->Throw(env->ExceptionOccurred());
+ return collides;
+ }
+
+ collides = (bool) notifyResult;
+ }
//add some additional logic here that modified 'collides'
return collides;
diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
index 51fa0fcea..9122e1bc6 100644
--- a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
+++ b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java
@@ -334,19 +334,20 @@ public class PhysicsSpace {
private void addCollisionEvent_native(PhysicsCollisionObject node, PhysicsCollisionObject node1, long manifoldPointObjectId) {
// System.out.println("addCollisionEvent:"+node.getObjectId()+" "+ node1.getObjectId());
collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, manifoldPointObjectId));
-
- // Notify group listeners
- if((node.getCollideWithGroups() & node1.getCollisionGroup()) > 0
- || (node1.getCollideWithGroups() & node.getCollisionGroup()) > 0){
- PhysicsCollisionGroupListener listener = collisionGroupListeners.get(node.getCollisionGroup());
- PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(node1.getCollisionGroup());
- if(listener != null){
- listener.collide(node, node1);
- }
- if(listener1 != null && node.getCollisionGroup() != node1.getCollisionGroup()){
- listener1.collide(node, node1);
+ }
+
+ private boolean notifyCollisionGroupListeners_native(PhysicsCollisionObject node, PhysicsCollisionObject node1){
+ PhysicsCollisionGroupListener listener = collisionGroupListeners.get(node.getCollisionGroup());
+ PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(node1.getCollisionGroup());
+ if(listener != null){
+ if(!listener.collide(node, node1)){
+ return false;
}
}
+ if(listener1 != null && node.getCollisionGroup() != node1.getCollisionGroup()){
+ return listener1.collide(node, node1);
+ }
+ return true;
}
/**
From 665908cdeef516307ff124f69d001d53e60bfb3c Mon Sep 17 00:00:00 2001
From: MeFisto94
Date: Thu, 24 Mar 2016 20:52:17 +0100
Subject: [PATCH 05/77] Improved the MotionEvent Cloning to not throw an NPE or
edit constant Vectors
---
.../jme3/cinematic/events/MotionEvent.java | 20 +++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
index fc277880b..af4769841 100644
--- a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
+++ b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java
@@ -64,9 +64,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
protected int currentWayPoint;
protected float currentValue;
protected Vector3f direction = new Vector3f();
- protected Vector3f lookAt = Vector3f.ZERO;
+ protected Vector3f lookAt = null;
protected Vector3f upVector = Vector3f.UNIT_Y;
- protected Quaternion rotation = Quaternion.IDENTITY;
+ protected Quaternion rotation = null;
protected Direction directionType = Direction.None;
protected MotionPath path;
private boolean isControl = true;
@@ -213,9 +213,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
public void write(JmeExporter ex) throws IOException {
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
- oc.write(lookAt, "lookAt", Vector3f.ZERO);
+ oc.write(lookAt, "lookAt", null);
oc.write(upVector, "upVector", Vector3f.UNIT_Y);
- oc.write(rotation, "rotation", Quaternion.IDENTITY);
+ oc.write(rotation, "rotation", null);
oc.write(directionType, "directionType", Direction.None);
oc.write(path, "path", null);
}
@@ -224,9 +224,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
public void read(JmeImporter im) throws IOException {
super.read(im);
InputCapsule in = im.getCapsule(this);
- lookAt = (Vector3f) in.readSavable("lookAt", Vector3f.ZERO);
+ lookAt = (Vector3f) in.readSavable("lookAt", null);
upVector = (Vector3f) in.readSavable("upVector", Vector3f.UNIT_Y);
- rotation = (Quaternion) in.readSavable("rotation", Quaternion.IDENTITY);
+ rotation = (Quaternion) in.readSavable("rotation", null);
directionType = in.readEnum("directionType", Direction.class, Direction.None);
path = (MotionPath) in.readSavable("path", null);
}
@@ -283,9 +283,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
control.currentWayPoint = currentWayPoint;
control.currentValue = currentValue;
control.direction = direction.clone();
- control.lookAt = lookAt.clone();
+ control.lookAt = lookAt;
control.upVector = upVector.clone();
- control.rotation = rotation.clone();
+ control.rotation = rotation;
control.initialDuration = initialDuration;
control.speed = speed;
control.loopMode = loopMode;
@@ -302,9 +302,9 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC
control.currentWayPoint = currentWayPoint;
control.currentValue = currentValue;
control.direction = direction.clone();
- control.lookAt = lookAt.clone();
+ control.lookAt = lookAt;
control.upVector = upVector.clone();
- control.rotation = rotation.clone();
+ control.rotation = rotation;
control.initialDuration = initialDuration;
control.speed = speed;
control.loopMode = loopMode;
From 6e999aa79bf3fc37e8a1d7836b0b1933db3ced81 Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sat, 26 Mar 2016 03:45:34 -0400
Subject: [PATCH 06/77] Tired of committing around the generated
version.prpoerties file... so I'm remove it and fixing it with a .gitignore.
---
.../src/main/resources/com/jme3/system/.gitignore | 1 +
.../resources/com/jme3/system/version.properties | 12 ------------
2 files changed, 1 insertion(+), 12 deletions(-)
create mode 100644 jme3-core/src/main/resources/com/jme3/system/.gitignore
delete mode 100644 jme3-core/src/main/resources/com/jme3/system/version.properties
diff --git a/jme3-core/src/main/resources/com/jme3/system/.gitignore b/jme3-core/src/main/resources/com/jme3/system/.gitignore
new file mode 100644
index 000000000..13ee572a7
--- /dev/null
+++ b/jme3-core/src/main/resources/com/jme3/system/.gitignore
@@ -0,0 +1 @@
+version.properties
diff --git a/jme3-core/src/main/resources/com/jme3/system/version.properties b/jme3-core/src/main/resources/com/jme3/system/version.properties
deleted file mode 100644
index ae20006c0..000000000
--- a/jme3-core/src/main/resources/com/jme3/system/version.properties
+++ /dev/null
@@ -1,12 +0,0 @@
-# THIS IS AN AUTO-GENERATED FILE..
-# DO NOT MODIFY!
-build.date=2016-03-25
-git.revision=0
-git.branch=unknown
-git.hash=
-git.hash.short=
-git.tag=
-name.full=jMonkeyEngine 3.1.0-UNKNOWN
-version.full=3.1.0-UNKNOWN
-version.number=3.1.0
-version.tag=SNAPSHOT
\ No newline at end of file
From 7665fef2dede5703c928ec9bef0a962c44324f47 Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sat, 26 Mar 2016 03:46:48 -0400
Subject: [PATCH 07/77] ParticleEmitter and related classes (ugh) now implement
JmeCloneable. It hasn't replaced the old clone() method yet and is still
untested.
---
.../java/com/jme3/effect/ParticleEmitter.java | 304 ++++++++++--------
.../DefaultParticleInfluencer.java | 35 +-
.../influencers/EmptyParticleInfluencer.java | 21 ++
.../NewtonianParticleInfluencer.java | 2 +
.../influencers/ParticleInfluencer.java | 3 +-
.../influencers/RadialParticleInfluencer.java | 19 +-
.../jme3/effect/shapes/EmitterBoxShape.java | 23 ++
.../effect/shapes/EmitterMeshVertexShape.java | 25 +-
.../jme3/effect/shapes/EmitterPointShape.java | 22 ++
.../com/jme3/effect/shapes/EmitterShape.java | 3 +-
.../effect/shapes/EmitterSphereShape.java | 22 ++
11 files changed, 336 insertions(+), 143 deletions(-)
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 d53273c4e..3baf8feb6 100644
--- a/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java
+++ b/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java
@@ -65,12 +65,12 @@ import java.io.IOException;
* Particle emitters can be used to simulate various kinds of phenomena,
* such as fire, smoke, explosions and much more.
*
- * Particle emitters have many properties which are used to control the
- * simulation. The interpretation of these properties depends on the
+ * Particle emitters have many properties which are used to control the
+ * simulation. The interpretation of these properties depends on the
* {@link ParticleInfluencer} that has been assigned to the emitter via
* {@link ParticleEmitter#setParticleInfluencer(com.jme3.effect.influencers.ParticleInfluencer) }.
* By default the implementation {@link DefaultParticleInfluencer} is used.
- *
+ *
* @author Kirill Vainer
*/
public class ParticleEmitter extends Geometry {
@@ -100,7 +100,7 @@ public class ParticleEmitter extends Geometry {
private Vector3f faceNormal = new Vector3f(Vector3f.NAN);
private int imagesX = 1;
private int imagesY = 1;
-
+
private ColorRGBA startColor = new ColorRGBA(0.4f, 0.4f, 0.4f, 0.5f);
private ColorRGBA endColor = new ColorRGBA(0.1f, 0.1f, 0.1f, 0.0f);
private float startSize = 0.2f;
@@ -127,20 +127,20 @@ public class ParticleEmitter extends Geometry {
// fixed automatically by ParticleEmitter.clone() method.
}
- @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.parentEmitter = cloner.clone(parentEmitter);
}
-
+
public void setSpatial(Spatial spatial) {
}
@@ -211,6 +211,42 @@ public class ParticleEmitter extends Geometry {
return clone;
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.shape = cloner.clone(shape);
+ this.control = cloner.clone(control);
+ this.faceNormal = cloner.clone(faceNormal);
+ this.startColor = cloner.clone(startColor);
+ this.endColor = cloner.clone(endColor);
+ this.particleInfluencer = cloner.clone(particleInfluencer);
+
+ // change in behavior: gravity was not cloned before -pspeed
+ this.gravity = cloner.clone(gravity);
+
+ // So, simply setting the mesh type will cause all kinds of things
+ // to happen:
+ // 1) the new mesh gets created.
+ // 2) it is set to the Geometry
+ // 3) the particles array is recreated because setNumParticles()
+ //
+ // ...so this should be equivalent but simpler than half of the old clone()
+ // method. Note: we do not ever want to share particleMesh so we do not
+ // clone it at all.
+ setMeshType(meshType);
+
+ // change in behavior: temp and lastPos were not cloned before...
+ // perhaps because it was believed that 'transient' fields were exluded
+ // from cloning? (they aren't)
+ // If it was ok for these to be shared because of how they are used
+ // then they could just as well be made static... else I think it's clearer
+ // to clone them.
+ this.temp = cloner.clone(temp);
+ this.lastPos = cloner.clone(lastPos);
+ }
+
public ParticleEmitter(String name, Type type, int numParticles) {
super(name);
setBatchHint(BatchHint.Never);
@@ -225,7 +261,7 @@ public class ParticleEmitter extends Geometry {
meshType = type;
- // Must create clone of shape/influencer so that a reference to a static is
+ // Must create clone of shape/influencer so that a reference to a static is
// not maintained
shape = shape.deepClone();
particleInfluencer = particleInfluencer.clone();
@@ -267,10 +303,10 @@ public class ParticleEmitter extends Geometry {
/**
* Set the {@link ParticleInfluencer} to influence this particle emitter.
- *
- * @param particleInfluencer the {@link ParticleInfluencer} to influence
+ *
+ * @param particleInfluencer the {@link ParticleInfluencer} to influence
* this particle emitter.
- *
+ *
* @see ParticleInfluencer
*/
public void setParticleInfluencer(ParticleInfluencer particleInfluencer) {
@@ -278,12 +314,12 @@ public class ParticleEmitter extends Geometry {
}
/**
- * Returns the {@link ParticleInfluencer} that influences this
+ * Returns the {@link ParticleInfluencer} that influences this
* particle emitter.
- *
- * @return the {@link ParticleInfluencer} that influences this
+ *
+ * @return the {@link ParticleInfluencer} that influences this
* particle emitter.
- *
+ *
* @see ParticleInfluencer
*/
public ParticleInfluencer getParticleInfluencer() {
@@ -292,12 +328,12 @@ public class ParticleEmitter extends Geometry {
/**
* Returns the mesh type used by the particle emitter.
- *
- *
+ *
+ *
* @return the mesh type used by the particle emitter.
- *
+ *
* @see #setMeshType(com.jme3.effect.ParticleMesh.Type)
- * @see ParticleEmitter#ParticleEmitter(java.lang.String, com.jme3.effect.ParticleMesh.Type, int)
+ * @see ParticleEmitter#ParticleEmitter(java.lang.String, com.jme3.effect.ParticleMesh.Type, int)
*/
public ParticleMesh.Type getMeshType() {
return meshType;
@@ -325,26 +361,26 @@ public class ParticleEmitter extends Geometry {
}
/**
- * Returns true if particles should spawn in world space.
- *
- * @return true if particles should spawn in world space.
- *
- * @see ParticleEmitter#setInWorldSpace(boolean)
+ * Returns true if particles should spawn in world space.
+ *
+ * @return true if particles should spawn in world space.
+ *
+ * @see ParticleEmitter#setInWorldSpace(boolean)
*/
public boolean isInWorldSpace() {
return worldSpace;
}
/**
- * Set to true if particles should spawn in world space.
- *
+ * Set to true if particles should spawn in world space.
+ *
*
If set to true and the particle emitter is moved in the scene,
* then particles that have already spawned won't be effected by this
* motion. If set to false, the particles will emit in local space
* and when the emitter is moved, so are all the particles that
* were emitted previously.
- *
- * @param worldSpace true if particles should spawn in world space.
+ *
+ * @param worldSpace true if particles should spawn in world space.
*/
public void setInWorldSpace(boolean worldSpace) {
this.setIgnoreTransform(worldSpace);
@@ -353,7 +389,7 @@ public class ParticleEmitter extends Geometry {
/**
* Returns the number of visible particles (spawned but not dead).
- *
+ *
* @return the number of visible particles
*/
public int getNumVisibleParticles() {
@@ -365,7 +401,7 @@ public class ParticleEmitter extends Geometry {
* Set the maximum amount of particles that
* can exist at the same time with this emitter.
* Calling this method many times is not recommended.
- *
+ *
* @param numParticles the maximum amount of particles that
* can exist at the same time with this emitter.
*/
@@ -387,13 +423,13 @@ public class ParticleEmitter extends Geometry {
/**
* Returns a list of all particles (shouldn't be used in most cases).
- *
+ *
*
* This includes both existing and non-existing particles.
* The size of the array is set to the numParticles value
* specified in the constructor or {@link ParticleEmitter#setNumParticles(int) }
- * method.
- *
+ * method.
+ *
* @return a list of all particles.
*/
public Particle[] getParticles() {
@@ -401,11 +437,11 @@ public class ParticleEmitter extends Geometry {
}
/**
- * Get the normal which particles are facing.
- *
- * @return the normal which particles are facing.
- *
- * @see ParticleEmitter#setFaceNormal(com.jme3.math.Vector3f)
+ * Get the normal which particles are facing.
+ *
+ * @return the normal which particles are facing.
+ *
+ * @see ParticleEmitter#setFaceNormal(com.jme3.math.Vector3f)
*/
public Vector3f getFaceNormal() {
if (Vector3f.isValidVector(faceNormal)) {
@@ -416,8 +452,8 @@ public class ParticleEmitter extends Geometry {
}
/**
- * Sets the normal which particles are facing.
- *
+ * Sets the normal which particles are facing.
+ *
*
By default, particles
* will face the camera, but for some effects (e.g shockwave) it may
* be necessary to face a specific direction instead. To restore
@@ -437,10 +473,10 @@ public class ParticleEmitter extends Geometry {
/**
* Returns the rotation speed in radians/sec for particles.
- *
+ *
* @return the rotation speed in radians/sec for particles.
- *
- * @see ParticleEmitter#setRotateSpeed(float)
+ *
+ * @see ParticleEmitter#setRotateSpeed(float)
*/
public float getRotateSpeed() {
return rotateSpeed;
@@ -449,7 +485,7 @@ public class ParticleEmitter extends Geometry {
/**
* Set the rotation speed in radians/sec for particles
* spawned after the invocation of this method.
- *
+ *
* @param rotateSpeed the rotation speed in radians/sec for particles
* spawned after the invocation of this method.
*/
@@ -459,12 +495,12 @@ public class ParticleEmitter extends Geometry {
/**
* Returns true if every particle spawned
- * should have a random facing angle.
- *
+ * should have a random facing angle.
+ *
* @return true if every particle spawned
- * should have a random facing angle.
- *
- * @see ParticleEmitter#setRandomAngle(boolean)
+ * should have a random facing angle.
+ *
+ * @see ParticleEmitter#setRandomAngle(boolean)
*/
public boolean isRandomAngle() {
return randomAngle;
@@ -472,8 +508,8 @@ public class ParticleEmitter extends Geometry {
/**
* Set to true if every particle spawned
- * should have a random facing angle.
- *
+ * should have a random facing angle.
+ *
* @param randomAngle if every particle spawned
* should have a random facing angle.
*/
@@ -484,11 +520,11 @@ public class ParticleEmitter extends Geometry {
/**
* Returns true if every particle spawned should get a random
* image.
- *
+ *
* @return True if every particle spawned should get a random
* image.
- *
- * @see ParticleEmitter#setSelectRandomImage(boolean)
+ *
+ * @see ParticleEmitter#setSelectRandomImage(boolean)
*/
public boolean isSelectRandomImage() {
return selectRandomImage;
@@ -498,7 +534,7 @@ public class ParticleEmitter extends Geometry {
* Set to true if every particle spawned
* should get a random image from a pool of images constructed from
* the texture, with X by Y possible images.
- *
+ *
*
By default, X and Y are equal
* to 1, thus allowing only 1 possible image to be selected, but if the
* particle is configured with multiple images by using {@link ParticleEmitter#setImagesX(int) }
@@ -506,7 +542,7 @@ public class ParticleEmitter extends Geometry {
* can be selected. Setting to false will cause each particle to have an animation
* of images displayed, starting at image 1, and going until image X*Y when
* the particle reaches its end of life.
- *
+ *
* @param selectRandomImage True if every particle spawned should get a random
* image.
*/
@@ -516,10 +552,10 @@ public class ParticleEmitter extends Geometry {
/**
* Check if particles spawned should face their velocity.
- *
+ *
* @return True if particles spawned should face their velocity.
- *
- * @see ParticleEmitter#setFacingVelocity(boolean)
+ *
+ * @see ParticleEmitter#setFacingVelocity(boolean)
*/
public boolean isFacingVelocity() {
return facingVelocity;
@@ -528,11 +564,11 @@ public class ParticleEmitter extends Geometry {
/**
* Set to true if particles spawned should face
* their velocity (or direction to which they are moving towards).
- *
+ *
*
This is typically used for e.g spark effects.
- *
+ *
* @param followVelocity True if particles spawned should face their velocity.
- *
+ *
*/
public void setFacingVelocity(boolean followVelocity) {
this.facingVelocity = followVelocity;
@@ -540,10 +576,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the end color of the particles spawned.
- *
+ *
* @return the end color of the particles spawned.
- *
- * @see ParticleEmitter#setEndColor(com.jme3.math.ColorRGBA)
+ *
+ * @see ParticleEmitter#setEndColor(com.jme3.math.ColorRGBA)
*/
public ColorRGBA getEndColor() {
return endColor;
@@ -551,12 +587,12 @@ public class ParticleEmitter extends Geometry {
/**
* Set the end color of the particles spawned.
- *
+ *
*
The
* particle color at any time is determined by blending the start color
* and end color based on the particle's current time of life relative
* to its end of life.
- *
+ *
* @param endColor the end color of the particles spawned.
*/
public void setEndColor(ColorRGBA endColor) {
@@ -565,10 +601,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the end size of the particles spawned.
- *
+ *
* @return the end size of the particles spawned.
- *
- * @see ParticleEmitter#setEndSize(float)
+ *
+ * @see ParticleEmitter#setEndSize(float)
*/
public float getEndSize() {
return endSize;
@@ -576,12 +612,12 @@ public class ParticleEmitter extends Geometry {
/**
* Set the end size of the particles spawned.
- *
+ *
*
The
* particle size at any time is determined by blending the start size
* and end size based on the particle's current time of life relative
* to its end of life.
- *
+ *
* @param endSize the end size of the particles spawned.
*/
public void setEndSize(float endSize) {
@@ -590,10 +626,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the gravity vector.
- *
+ *
* @return the gravity vector.
- *
- * @see ParticleEmitter#setGravity(com.jme3.math.Vector3f)
+ *
+ * @see ParticleEmitter#setGravity(com.jme3.math.Vector3f)
*/
public Vector3f getGravity() {
return gravity;
@@ -601,7 +637,7 @@ public class ParticleEmitter extends Geometry {
/**
* This method sets the gravity vector.
- *
+ *
* @param gravity the gravity vector
*/
public void setGravity(Vector3f gravity) {
@@ -610,7 +646,7 @@ public class ParticleEmitter extends Geometry {
/**
* Sets the gravity vector.
- *
+ *
* @param x the x component of the gravity vector
* @param y the y component of the gravity vector
* @param z the z component of the gravity vector
@@ -623,10 +659,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the high value of life.
- *
+ *
* @return the high value of life.
- *
- * @see ParticleEmitter#setHighLife(float)
+ *
+ * @see ParticleEmitter#setHighLife(float)
*/
public float getHighLife() {
return highLife;
@@ -634,10 +670,10 @@ public class ParticleEmitter extends Geometry {
/**
* Set the high value of life.
- *
+ *
*
The particle's lifetime/expiration
* is determined by randomly selecting a time between low life and high life.
- *
+ *
* @param highLife the high value of life.
*/
public void setHighLife(float highLife) {
@@ -646,10 +682,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the number of images along the X axis (width).
- *
+ *
* @return the number of images along the X axis (width).
- *
- * @see ParticleEmitter#setImagesX(int)
+ *
+ * @see ParticleEmitter#setImagesX(int)
*/
public int getImagesX() {
return imagesX;
@@ -657,11 +693,11 @@ public class ParticleEmitter extends Geometry {
/**
* Set the number of images along the X axis (width).
- *
+ *
*
To determine
* how multiple particle images are selected and used, see the
* {@link ParticleEmitter#setSelectRandomImage(boolean) } method.
- *
+ *
* @param imagesX the number of images along the X axis (width).
*/
public void setImagesX(int imagesX) {
@@ -671,10 +707,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the number of images along the Y axis (height).
- *
+ *
* @return the number of images along the Y axis (height).
- *
- * @see ParticleEmitter#setImagesY(int)
+ *
+ * @see ParticleEmitter#setImagesY(int)
*/
public int getImagesY() {
return imagesY;
@@ -682,10 +718,10 @@ public class ParticleEmitter extends Geometry {
/**
* Set the number of images along the Y axis (height).
- *
+ *
*
To determine how multiple particle images are selected and used, see the
* {@link ParticleEmitter#setSelectRandomImage(boolean) } method.
- *
+ *
* @param imagesY the number of images along the Y axis (height).
*/
public void setImagesY(int imagesY) {
@@ -695,10 +731,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the low value of life.
- *
+ *
* @return the low value of life.
- *
- * @see ParticleEmitter#setLowLife(float)
+ *
+ * @see ParticleEmitter#setLowLife(float)
*/
public float getLowLife() {
return lowLife;
@@ -706,10 +742,10 @@ public class ParticleEmitter extends Geometry {
/**
* Set the low value of life.
- *
+ *
*
The particle's lifetime/expiration
* is determined by randomly selecting a time between low life and high life.
- *
+ *
* @param lowLife the low value of life.
*/
public void setLowLife(float lowLife) {
@@ -719,11 +755,11 @@ public class ParticleEmitter extends Geometry {
/**
* Get the number of particles to spawn per
* second.
- *
+ *
* @return the number of particles to spawn per
* second.
- *
- * @see ParticleEmitter#setParticlesPerSec(float)
+ *
+ * @see ParticleEmitter#setParticlesPerSec(float)
*/
public float getParticlesPerSec() {
return particlesPerSec;
@@ -732,7 +768,7 @@ public class ParticleEmitter extends Geometry {
/**
* Set the number of particles to spawn per
* second.
- *
+ *
* @param particlesPerSec the number of particles to spawn per
* second.
*/
@@ -740,13 +776,13 @@ public class ParticleEmitter extends Geometry {
this.particlesPerSec = particlesPerSec;
timeDifference = 0;
}
-
+
/**
* Get the start color of the particles spawned.
- *
+ *
* @return the start color of the particles spawned.
- *
- * @see ParticleEmitter#setStartColor(com.jme3.math.ColorRGBA)
+ *
+ * @see ParticleEmitter#setStartColor(com.jme3.math.ColorRGBA)
*/
public ColorRGBA getStartColor() {
return startColor;
@@ -754,11 +790,11 @@ public class ParticleEmitter extends Geometry {
/**
* Set the start color of the particles spawned.
- *
+ *
*
The particle color at any time is determined by blending the start color
* and end color based on the particle's current time of life relative
* to its end of life.
- *
+ *
* @param startColor the start color of the particles spawned
*/
public void setStartColor(ColorRGBA startColor) {
@@ -767,10 +803,10 @@ public class ParticleEmitter extends Geometry {
/**
* Get the start color of the particles spawned.
- *
+ *
* @return the start color of the particles spawned.
- *
- * @see ParticleEmitter#setStartSize(float)
+ *
+ * @see ParticleEmitter#setStartSize(float)
*/
public float getStartSize() {
return startSize;
@@ -778,11 +814,11 @@ public class ParticleEmitter extends Geometry {
/**
* Set the start size of the particles spawned.
- *
+ *
*
The particle size at any time is determined by blending the start size
* and end size based on the particle's current time of life relative
* to its end of life.
- *
+ *
* @param startSize the start size of the particles spawned.
*/
public void setStartSize(float startSize) {
@@ -805,10 +841,10 @@ public class ParticleEmitter extends Geometry {
* gravity.
*
* @deprecated
- * This method is deprecated.
+ * This method is deprecated.
* Use ParticleEmitter.getParticleInfluencer().setInitialVelocity(initialVelocity); instead.
*
- * @see ParticleEmitter#setVelocityVariation(float)
+ * @see ParticleEmitter#setVelocityVariation(float)
* @see ParticleEmitter#setGravity(float)
*/
@Deprecated
@@ -818,7 +854,7 @@ public class ParticleEmitter extends Geometry {
/**
* @deprecated
- * This method is deprecated.
+ * This method is deprecated.
* Use ParticleEmitter.getParticleInfluencer().getVelocityVariation(); instead.
* @return the initial velocity variation factor
*/
@@ -833,9 +869,9 @@ public class ParticleEmitter extends Geometry {
* from 0 to 1, where 0 means particles are to spawn with exactly
* the velocity given in {@link ParticleEmitter#setStartVel(com.jme3.math.Vector3f) },
* and 1 means particles are to spawn with a completely random velocity.
- *
+ *
* @deprecated
- * This method is deprecated.
+ * This method is deprecated.
* Use ParticleEmitter.getParticleInfluencer().setVelocityVariation(variation); instead.
*/
@Deprecated
@@ -923,7 +959,7 @@ public class ParticleEmitter extends Geometry {
vars.release();
}
-
+
/**
* Instantly kills all active particles, after this method is called, all
* particles will be dead and no longer visible.
@@ -935,12 +971,12 @@ public class ParticleEmitter extends Geometry {
}
}
}
-
+
/**
* Kills the particle at the given index.
- *
+ *
* @param index The index of the particle to kill
- * @see #getParticles()
+ * @see #getParticles()
*/
public void killParticle(int index){
freeParticle(index);
@@ -995,7 +1031,7 @@ public class ParticleEmitter extends Geometry {
p.imageIndex = (int) (b * imagesX * imagesY);
}
}
-
+
private void updateParticleState(float tpf) {
// Force world transform to update
this.getWorldTransform();
@@ -1028,7 +1064,7 @@ public class ParticleEmitter extends Geometry {
firstUnUsed++;
}
}
-
+
// Spawns particles within the tpf timeslot with proper age
float interval = 1f / particlesPerSec;
float originalTpf = tpf;
@@ -1065,10 +1101,10 @@ public class ParticleEmitter extends Geometry {
/**
* Set to enable or disable the particle emitter
- *
+ *
*
When a particle is
* disabled, it will be "frozen in time" and not update.
- *
+ *
* @param enabled True to enable the particle emitter
*/
public void setEnabled(boolean enabled) {
@@ -1077,10 +1113,10 @@ public class ParticleEmitter extends Geometry {
/**
* Check if a particle emitter is enabled for update.
- *
+ *
* @return True if a particle emitter is enabled for update.
- *
- * @see ParticleEmitter#setEnabled(boolean)
+ *
+ * @see ParticleEmitter#setEnabled(boolean)
*/
public boolean isEnabled() {
return enabled;
@@ -1088,7 +1124,7 @@ public class ParticleEmitter extends Geometry {
/**
* Callback from Control.update(), do not use.
- * @param tpf
+ * @param tpf
*/
public void updateFromControl(float tpf) {
if (enabled) {
@@ -1098,9 +1134,9 @@ public class ParticleEmitter extends Geometry {
/**
* Callback from Control.render(), do not use.
- *
+ *
* @param rm
- * @param vp
+ * @param vp
*/
private void renderFromControl(RenderManager rm, ViewPort vp) {
Camera cam = vp.getCamera();
@@ -1236,7 +1272,7 @@ public class ParticleEmitter extends Geometry {
gravity.y = ic.readFloat("gravity", 0);
}
} else {
- // since the parentEmitter is not loaded, it must be
+ // since the parentEmitter is not loaded, it must be
// loaded separately
control = getControl(ParticleEmitterControl.class);
control.parentEmitter = this;
diff --git a/jme3-core/src/main/java/com/jme3/effect/influencers/DefaultParticleInfluencer.java b/jme3-core/src/main/java/com/jme3/effect/influencers/DefaultParticleInfluencer.java
index b52d8df80..9cd06f0e3 100644
--- a/jme3-core/src/main/java/com/jme3/effect/influencers/DefaultParticleInfluencer.java
+++ b/jme3-core/src/main/java/com/jme3/effect/influencers/DefaultParticleInfluencer.java
@@ -39,6 +39,8 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
/**
@@ -49,7 +51,7 @@ import java.io.IOException;
*/
public class DefaultParticleInfluencer implements ParticleInfluencer {
- //Version #1 : changed startVelocity to initialvelocity for consistency with accessors
+ //Version #1 : changed startVelocity to initialvelocity for consistency with accessors
//and also changed it in serialization
public static final int SAVABLE_VERSION = 1;
/** Temporary variable used to help with calculations. */
@@ -94,7 +96,7 @@ public class DefaultParticleInfluencer implements ParticleInfluencer {
initialVelocity = (Vector3f) ic.readSavable("startVelocity", Vector3f.ZERO.clone());
}else{
initialVelocity = (Vector3f) ic.readSavable("initialVelocity", Vector3f.ZERO.clone());
- }
+ }
velocityVariation = ic.readFloat("variation", 0.2f);
}
@@ -109,6 +111,35 @@ public class DefaultParticleInfluencer implements ParticleInfluencer {
}
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public Object jmeClone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.initialVelocity = cloner.clone(initialVelocity);
+
+ // Change in behavior: I'm cloning 'for real' the 'temp' field because
+ // otherwise it will be shared across all clones. Note: if this is
+ // ok because of how its used then it might as well be static and let
+ // everything share it.
+ // Note 2: transient fields _are_ cloned just like anything else so
+ // thinking it wouldn't get cloned is also not right.
+ // -pspeed
+ this.temp = cloner.clone(temp);
+ }
+
@Override
public void setInitialVelocity(Vector3f initialVelocity) {
this.initialVelocity.set(initialVelocity);
diff --git a/jme3-core/src/main/java/com/jme3/effect/influencers/EmptyParticleInfluencer.java b/jme3-core/src/main/java/com/jme3/effect/influencers/EmptyParticleInfluencer.java
index ccbc7e5e6..88e938430 100644
--- a/jme3-core/src/main/java/com/jme3/effect/influencers/EmptyParticleInfluencer.java
+++ b/jme3-core/src/main/java/com/jme3/effect/influencers/EmptyParticleInfluencer.java
@@ -36,6 +36,8 @@ import com.jme3.effect.shapes.EmitterShape;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
/**
@@ -83,4 +85,23 @@ public class EmptyParticleInfluencer implements ParticleInfluencer {
throw new AssertionError();
}
}
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public Object jmeClone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ }
}
diff --git a/jme3-core/src/main/java/com/jme3/effect/influencers/NewtonianParticleInfluencer.java b/jme3-core/src/main/java/com/jme3/effect/influencers/NewtonianParticleInfluencer.java
index b2f81f9a8..b0bc1be25 100644
--- a/jme3-core/src/main/java/com/jme3/effect/influencers/NewtonianParticleInfluencer.java
+++ b/jme3-core/src/main/java/com/jme3/effect/influencers/NewtonianParticleInfluencer.java
@@ -39,6 +39,8 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
/**
diff --git a/jme3-core/src/main/java/com/jme3/effect/influencers/ParticleInfluencer.java b/jme3-core/src/main/java/com/jme3/effect/influencers/ParticleInfluencer.java
index 5e3532bb0..4f322df74 100644
--- a/jme3-core/src/main/java/com/jme3/effect/influencers/ParticleInfluencer.java
+++ b/jme3-core/src/main/java/com/jme3/effect/influencers/ParticleInfluencer.java
@@ -36,12 +36,13 @@ import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.shapes.EmitterShape;
import com.jme3.export.Savable;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.JmeCloneable;
/**
* An interface that defines the methods to affect initial velocity of the particles.
* @author Marcin Roguski (Kaelthas)
*/
-public interface ParticleInfluencer extends Savable, Cloneable {
+public interface ParticleInfluencer extends Savable, Cloneable, JmeCloneable {
/**
* This method influences the particle.
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 87c5ce507..0cacfe5b0 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
@@ -38,6 +38,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
import java.io.IOException;
/**
@@ -81,7 +82,7 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
/**
* the origin used for computing the radial velocity direction
- * @param origin
+ * @param origin
*/
public void setOrigin(Vector3f origin) {
this.origin = origin;
@@ -97,7 +98,7 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
/**
* the radial velocity
- * @param radialVelocity
+ * @param radialVelocity
*/
public void setRadialVelocity(float radialVelocity) {
this.radialVelocity = radialVelocity;
@@ -105,7 +106,7 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
/**
* nullify y component of particle velocity to make the effect expand only on x and z axis
- * @return
+ * @return
*/
public boolean isHorizontal() {
return horizontal;
@@ -113,12 +114,22 @@ public class RadialParticleInfluencer extends DefaultParticleInfluencer {
/**
* nullify y component of particle velocity to make the effect expand only on x and z axis
- * @param horizontal
+ * @param horizontal
*/
public void setHorizontal(boolean horizontal) {
this.horizontal = horizontal;
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ // Change in behavior: the old origin was not cloned -pspeed
+ this.origin = cloner.clone(origin);
+ }
+
+
@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterBoxShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterBoxShape.java
index 63323db7b..6b29843c9 100644
--- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterBoxShape.java
+++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterBoxShape.java
@@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
public class EmitterBoxShape implements EmitterShape {
@@ -86,6 +88,27 @@ public class EmitterBoxShape implements EmitterShape {
}
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public Object jmeClone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.min = cloner.clone(min);
+ this.len = cloner.clone(len);
+ }
+
public Vector3f getMin() {
return min;
}
diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java
index 0a5ba128a..b996e63cb 100644
--- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java
+++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java
@@ -40,6 +40,8 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -168,6 +170,27 @@ public class EmitterMeshVertexShape implements EmitterShape {
}
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public Object jmeClone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.vertices = cloner.clone(vertices);
+ this.normals = cloner.clone(normals);
+ }
+
@Override
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
@@ -180,7 +203,7 @@ public class EmitterMeshVertexShape implements EmitterShape {
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
this.vertices = ic.readSavableArrayList("vertices", null);
-
+
List> tmpNormals = ic.readSavableArrayList("normals", null);
if (tmpNormals != null){
this.normals = tmpNormals;
diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterPointShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterPointShape.java
index 9f7e71107..e33691101 100644
--- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterPointShape.java
+++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterPointShape.java
@@ -35,6 +35,8 @@ import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
public class EmitterPointShape implements EmitterShape {
@@ -59,6 +61,26 @@ public class EmitterPointShape implements EmitterShape {
}
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public Object jmeClone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.point = cloner.clone(point);
+ }
+
@Override
public void getRandomPoint(Vector3f store) {
store.set(point);
diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterShape.java
index bdecd5b5f..f247412d0 100644
--- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterShape.java
+++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterShape.java
@@ -33,12 +33,13 @@ package com.jme3.effect.shapes;
import com.jme3.export.Savable;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.JmeCloneable;
/**
* This interface declares methods used by all shapes that represent particle emitters.
* @author Kirill
*/
-public interface EmitterShape extends Savable, Cloneable {
+public interface EmitterShape extends Savable, Cloneable, JmeCloneable {
/**
* This method fills in the initial position of the particle.
diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterSphereShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterSphereShape.java
index 770ba6c9d..99d76205d 100644
--- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterSphereShape.java
+++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterSphereShape.java
@@ -37,6 +37,8 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
public class EmitterSphereShape implements EmitterShape {
@@ -71,6 +73,26 @@ public class EmitterSphereShape implements EmitterShape {
}
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public Object jmeClone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.center = cloner.clone(center);
+ }
+
@Override
public void getRandomPoint(Vector3f store) {
do {
From 3f1c696e2674f132fe989ec171c5c041d56380e1 Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sat, 26 Mar 2016 03:55:58 -0400
Subject: [PATCH 08/77] Adding a comment about the strange shared fields in
BitmapTextPage that I'm not going to touch with a ten foot pole.
---
jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
index 58dc87550..82d27aa65 100644
--- a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
+++ b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
@@ -123,6 +123,13 @@ class BitmapTextPage extends Geometry {
return clone;
}
+ // Here is where one might add JmeCloneable related stuff except
+ // the old clone() method doesn't actually bother to clone anything.
+ // The arrays and the pageQuads are shared across all BitmapTextPage
+ // clones and it doesn't seem to bother anything. That means the
+ // fields could probably just as well be static... but this code is
+ // all very fragile. I'm not tipping that particular boat today. -pspeed
+
void assemble(Letters quads) {
pageQuads.clear();
quads.rewind();
From 2f246b25bbd5648d9d343f75a98847695a3a3226 Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sat, 26 Mar 2016 04:08:51 -0400
Subject: [PATCH 09/77] Added cloneFields() method to BitmapText thought it's
probably fruitless since BitmapText isn't even properly saveable and couldn't
possibly have worked for any dynamic text with the old clone() method. Also a
bunch of white space changes removing spaces at the ends of lines.
---
.../main/java/com/jme3/font/BitmapText.java | 48 +++++++++-----
.../src/main/java/com/jme3/font/Letters.java | 62 +++++++++----------
2 files changed, 65 insertions(+), 45 deletions(-)
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 913bfe13a..20f5cec3b 100644
--- a/jme3-core/src/main/java/com/jme3/font/BitmapText.java
+++ b/jme3-core/src/main/java/com/jme3/font/BitmapText.java
@@ -38,6 +38,7 @@ import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
+import com.jme3.util.clone.Cloner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -84,6 +85,25 @@ public class BitmapText extends Node {
return clone;
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ for( int i = 0; i < textPages.length; i++ ) {
+ textPages[i] = cloner.clone(textPages[i]);
+ }
+ this.block = cloner.clone(block);
+
+ // Change in behavior: The 'letters' field was not cloned or recreated
+ // before. I'm not sure how this worked and suspect BitmapText was just
+ // not cloneable if you planned to change the text later. -pspeed
+ this.letters = new Letters(font, block, letters.getQuad().isRightToLeft());
+
+ // Just noticed BitmapText is not even writable/readable really...
+ // so I guess cloning doesn't come up that often.
+ }
+
public BitmapFont getFont() {
return font;
}
@@ -115,10 +135,10 @@ public class BitmapText extends Node {
*
* @param text String to change text to
*/
- public void setText(String text) {
+ public void setText(String text) {
text = text == null ? "" : text;
- if (text == block.getText() || block.getText().equals(text)) {
+ if (text == block.getText() || block.getText().equals(text)) {
return;
}
@@ -126,24 +146,24 @@ public class BitmapText extends Node {
The problem with the below block is that StringBlock carries
pretty much all of the text-related state of the BitmapText such
as size, text box, alignment, etc.
-
+
I'm not sure why this change was needed and the commit message was
- not entirely helpful because it purports to fix a problem that I've
+ not entirely helpful because it purports to fix a problem that I've
never encountered.
-
+
If block.setText("") doesn't do the right thing then that's where
the fix should go because StringBlock carries too much information to
be blown away every time. -pspeed
-
+
Change was made:
http://code.google.com/p/jmonkeyengine/source/detail?spec=svn9389&r=9389
Diff:
http://code.google.com/p/jmonkeyengine/source/diff?path=/trunk/engine/src/core/com/jme3/font/BitmapText.java&format=side&r=9389&old_path=/trunk/engine/src/core/com/jme3/font/BitmapText.java&old=8843
-
+
// If the text is empty, reset
if (text.isEmpty()) {
detachAllChildren();
-
+
for (int page = 0; page < textPages.length; page++) {
textPages[page] = new BitmapTextPage(font, true, page);
attachChild(textPages[page]);
@@ -153,7 +173,7 @@ public class BitmapText extends Node {
letters = new Letters(font, block, letters.getQuad().isRightToLeft());
}
*/
-
+
// Update the text content
block.setText(text);
letters.setText(text);
@@ -185,7 +205,7 @@ public class BitmapText extends Node {
letters.invalidate(); // TODO: Don't have to align.
needRefresh = true;
}
-
+
/**
* Sets an overall alpha that will be applied to all
* letters. If the alpha passed is -1 then alpha reverts
@@ -196,7 +216,7 @@ public class BitmapText extends Node {
public void setAlpha(float alpha) {
letters.setBaseAlpha(alpha);
needRefresh = true;
- }
+ }
public float getAlpha() {
return letters.getBaseAlpha();
@@ -414,17 +434,17 @@ public class BitmapText extends Node {
if( mp == null ) {
return null;
}
- return (ColorRGBA)mp.getValue();
+ return (ColorRGBA)mp.getValue();
}
public void render(RenderManager rm, ColorRGBA color) {
for (BitmapTextPage page : textPages) {
Material mat = page.getMaterial();
mat.setTexture("ColorMap", page.getTexture());
- //ColorRGBA original = getColor(mat, "Color");
+ //ColorRGBA original = getColor(mat, "Color");
//mat.setColor("Color", color);
mat.render(page, rm);
-
+
//if( original == null ) {
// mat.clearParam("Color");
//} else {
diff --git a/jme3-core/src/main/java/com/jme3/font/Letters.java b/jme3-core/src/main/java/com/jme3/font/Letters.java
index e8b8c8270..604f68785 100644
--- a/jme3-core/src/main/java/com/jme3/font/Letters.java
+++ b/jme3-core/src/main/java/com/jme3/font/Letters.java
@@ -53,7 +53,7 @@ class Letters {
private ColorRGBA baseColor = null;
private float baseAlpha = -1;
private String plainText;
-
+
Letters(BitmapFont font, StringBlock bound, boolean rightToLeft) {
final String text = bound.getText();
this.block = bound;
@@ -78,10 +78,10 @@ class Letters {
// Give the letter a default color if
// one has been provided.
l.setColor( baseColor );
- }
+ }
}
}
-
+
LinkedList ranges = colorTags.getTags();
if (!ranges.isEmpty()) {
for (int i = 0; i < ranges.size()-1; i++) {
@@ -92,7 +92,7 @@ class Letters {
Range end = ranges.getLast();
setColor(end.start, plainText.length(), end.color);
}
-
+
invalidate();
}
@@ -103,17 +103,17 @@ class Letters {
LetterQuad getTail() {
return tail;
}
-
+
void update() {
LetterQuad l = head;
int lineCount = 1;
BitmapCharacter ellipsis = font.getCharSet().getCharacter(block.getEllipsisChar());
float ellipsisWidth = ellipsis!=null? ellipsis.getWidth()*getScale(): 0;
-
+
while (!l.isTail()) {
if (l.isInvalid()) {
l.update(block);
-
+
if (l.isInvalid(block)) {
switch (block.getLineWrapMode()) {
case Character:
@@ -144,7 +144,7 @@ class Letters {
}
}
break;
- case NoWrap:
+ case NoWrap:
LetterQuad cursor = l.getPrevious();
while (cursor.isInvalid(block, ellipsisWidth) && !cursor.isLineStart()) {
cursor = cursor.getPrevious();
@@ -158,10 +158,10 @@ class Letters {
cursor = cursor.getNext();
}
break;
- case Clip:
+ case Clip:
// Clip the character that falls out of bounds
l.clip(block);
-
+
// Clear the rest up to the next line feed.
for( LetterQuad q = l.getNext(); !q.isTail() && !q.isLineFeed(); q = q.getNext() ) {
q.setBitmapChar(null);
@@ -178,12 +178,12 @@ class Letters {
}
l = l.getNext();
}
-
+
align();
block.setLineCount(lineCount);
rewind();
}
-
+
private void align() {
final Align alignment = block.getAlignment();
final VAlign valignment = block.getVerticalAlignment();
@@ -233,7 +233,7 @@ class Letters {
l.invalidate();
l.update(block); // TODO: update from l
}
-
+
float getCharacterX0() {
return current.getX0();
}
@@ -241,54 +241,54 @@ class Letters {
float getCharacterY0() {
return current.getY0();
}
-
+
float getCharacterX1() {
return current.getX1();
}
-
+
float getCharacterY1() {
return current.getY1();
}
-
+
float getCharacterAlignX() {
return current.getAlignX();
}
-
+
float getCharacterAlignY() {
return current.getAlignY();
}
-
+
float getCharacterWidth() {
return current.getWidth();
}
-
+
float getCharacterHeight() {
return current.getHeight();
}
-
+
public boolean nextCharacter() {
if (current.isTail())
return false;
current = current.getNext();
return true;
}
-
+
public int getCharacterSetPage() {
return current.getBitmapChar().getPage();
}
-
+
public LetterQuad getQuad() {
return current;
}
-
+
public void rewind() {
current = head;
}
-
+
public void invalidate() {
invalidate(head);
}
-
+
public void invalidate(LetterQuad cursor) {
totalWidth = -1;
totalHeight = -1;
@@ -298,7 +298,7 @@ class Letters {
cursor = cursor.getNext();
}
}
-
+
float getScale() {
return block.getSize() / font.getCharSet().getRenderedSize();
}
@@ -306,7 +306,7 @@ class Letters {
public boolean isPrintable() {
return current.getBitmapChar() != null;
}
-
+
float getTotalWidth() {
validateSize();
return totalWidth;
@@ -316,7 +316,7 @@ class Letters {
validateSize();
return totalHeight;
}
-
+
void validateSize() {
if (totalWidth < 0) {
LetterQuad l = head;
@@ -371,11 +371,11 @@ class Letters {
cursor = cursor.getNext();
}
}
-
+
float getBaseAlpha() {
return baseAlpha;
}
-
+
void setBaseAlpha( float alpha ) { this.baseAlpha = alpha;
colorTags.setBaseAlpha(alpha);
@@ -409,7 +409,7 @@ class Letters {
setColor(end.start, plainText.length(), end.color);
}
}
-
+
invalidate();
}
From eda92656dd3f16992b7d551322514a874033e06e Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sat, 26 Mar 2016 04:19:59 -0400
Subject: [PATCH 10/77] Updated AudioNode with a JmeCloneable cloneFields()
method to clone its fields. Some small change in behavior since the new
methods will clone the filters, too, to avoid 'user surprise'.
---
.../main/java/com/jme3/audio/AudioNode.java | 215 ++++++++++--------
1 file changed, 119 insertions(+), 96 deletions(-)
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 2c5cc5b98..bf4705d87 100644
--- a/jme3-core/src/main/java/com/jme3/audio/AudioNode.java
+++ b/jme3-core/src/main/java/com/jme3/audio/AudioNode.java
@@ -41,26 +41,27 @@ import com.jme3.export.OutputCapsule;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.util.PlaceholderAssets;
+import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * An AudioNode is a scene Node which can play audio assets.
- *
- * An AudioNode is either positional or ambient, with positional being the
- * default. Once a positional node is attached to the scene, its location and
- * velocity relative to the {@link Listener} affect how it sounds when played.
- * Positional nodes can only play monoaural (single-channel) assets, not stereo
- * ones.
- *
- * An ambient AudioNode plays in "headspace", meaning that the node's location
- * and velocity do not affect how it sounds when played. Ambient audio nodes can
- * play stereo assets.
- *
- * The "positional" property of an AudioNode can be set via
+ * An AudioNode is a scene Node which can play audio assets.
+ *
+ * An AudioNode is either positional or ambient, with positional being the
+ * default. Once a positional node is attached to the scene, its location and
+ * velocity relative to the {@link Listener} affect how it sounds when played.
+ * Positional nodes can only play monoaural (single-channel) assets, not stereo
+ * ones.
+ *
+ * An ambient AudioNode plays in "headspace", meaning that the node's location
+ * and velocity do not affect how it sounds when played. Ambient audio nodes can
+ * play stereo assets.
+ *
+ * The "positional" property of an AudioNode can be set via
* {@link AudioNode#setPositional(boolean) }.
- *
+ *
* @author normenhansen
* @author Kirill Vainer
*/
@@ -99,15 +100,15 @@ public class AudioNode extends Node implements AudioSource {
* {@link AudioNode#play() } is called.
*/
Playing,
-
+
/**
* The audio node is currently paused.
*/
Paused,
-
+
/**
* The audio node is currently stopped.
- * This will be set if {@link AudioNode#stop() } is called
+ * This will be set if {@link AudioNode#stop() } is called
* or the audio has reached the end of the file.
*/
Stopped,
@@ -121,14 +122,14 @@ public class AudioNode extends Node implements AudioSource {
/**
* Creates a new AudioNode with the given data and key.
- *
+ *
* @param audioData The audio data contains the audio track to play.
* @param audioKey The audio key that was used to load the AudioData
*/
public AudioNode(AudioData audioData, AudioKey audioKey) {
setAudioData(audioData, audioKey);
}
-
+
/**
* Creates a new AudioNode with the given audio file.
* @param assetManager The asset manager to use to load the audio file
@@ -142,16 +143,16 @@ public class AudioNode extends Node implements AudioSource {
/**
* Creates a new AudioNode with the given audio file.
- *
+ *
* @param assetManager The asset manager to use to load the audio file
* @param name The filename of the audio file
- * @param stream If true, the audio will be streamed gradually from disk,
+ * @param stream If true, the audio will be streamed gradually from disk,
* otherwise, it will be buffered.
* @param streamCache If stream is also true, then this specifies if
* the stream cache is used. When enabled, the audio stream will
- * be read entirely but not decoded, allowing features such as
+ * be read entirely but not decoded, allowing features such as
* seeking, looping and determining duration.
- *
+ *
* @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead
*/
public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) {
@@ -161,12 +162,12 @@ public class AudioNode extends Node implements AudioSource {
/**
* Creates a new AudioNode with the given audio file.
- *
+ *
* @param assetManager The asset manager to use to load the audio file
* @param name The filename of the audio file
- * @param stream If true, the audio will be streamed gradually from disk,
+ * @param stream If true, the audio will be streamed gradually from disk,
* otherwise, it will be buffered.
- *
+ *
* @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead
*/
public AudioNode(AssetManager assetManager, String name, boolean stream) {
@@ -175,20 +176,20 @@ public class AudioNode extends Node implements AudioSource {
/**
* Creates a new AudioNode with the given audio file.
- *
+ *
* @param audioRenderer The audio renderer to use for playing. Cannot be null.
* @param assetManager The asset manager to use to load the audio file
* @param name The filename of the audio file
- *
+ *
* @deprecated AudioRenderer parameter is ignored.
*/
public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) {
this(assetManager, name, DataType.Buffer);
}
-
+
/**
* Creates a new AudioNode with the given audio file.
- *
+ *
* @param assetManager The asset manager to use to load the audio file
* @param name The filename of the audio file
* @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType) } instead
@@ -196,14 +197,14 @@ public class AudioNode extends Node implements AudioSource {
public AudioNode(AssetManager assetManager, String name) {
this(assetManager, name, DataType.Buffer);
}
-
+
protected AudioRenderer getRenderer() {
AudioRenderer result = AudioContext.getAudioRenderer();
if( result == null )
throw new IllegalStateException( "No audio renderer available, make sure call is being performed on render thread." );
- return result;
+ return result;
}
-
+
/**
* Start playing the audio.
*/
@@ -217,7 +218,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* Start playing an instance of this audio. This method can be used
* to play the same AudioNode multiple times. Note
- * that changes to the parameters of this AudioNode will not effect the
+ * that changes to the parameters of this AudioNode will not effect the
* instances already playing.
*/
public void playInstance(){
@@ -226,21 +227,21 @@ public class AudioNode extends Node implements AudioSource {
}
getRenderer().playSourceInstance(this);
}
-
+
/**
* Stop playing the audio that was started with {@link AudioNode#play() }.
*/
public void stop(){
getRenderer().stopSource(this);
}
-
+
/**
* Pause the audio that was started with {@link AudioNode#play() }.
*/
public void pause(){
getRenderer().pauseSource(this);
}
-
+
/**
* Do not use.
*/
@@ -261,7 +262,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return The {#link Filter dry filter} that is set.
- * @see AudioNode#setDryFilter(com.jme3.audio.Filter)
+ * @see AudioNode#setDryFilter(com.jme3.audio.Filter)
*/
public Filter getDryFilter() {
return dryFilter;
@@ -269,14 +270,14 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the dry filter to use for this audio node.
- *
- * When {@link AudioNode#setReverbEnabled(boolean) reverb} is used,
- * the dry filter will only influence the "dry" portion of the audio,
+ *
+ * When {@link AudioNode#setReverbEnabled(boolean) reverb} is used,
+ * the dry filter will only influence the "dry" portion of the audio,
* e.g. not the reverberated parts of the AudioNode playing.
- *
+ *
* See the relevent documentation for the {@link Filter} to determine
* the effect.
- *
+ *
* @param dryFilter The filter to set, or null to disable dry filter.
*/
public void setDryFilter(Filter dryFilter) {
@@ -289,7 +290,7 @@ public class AudioNode extends Node implements AudioSource {
* Set the audio data to use for the audio. Note that this method
* can only be called once, if for example the audio node was initialized
* without an {@link AudioData}.
- *
+ *
* @param audioData The audio data contains the audio track to play.
* @param audioKey The audio key that was used to load the AudioData
*/
@@ -303,7 +304,7 @@ public class AudioNode extends Node implements AudioSource {
}
/**
- * @return The {@link AudioData} set previously with
+ * @return The {@link AudioData} set previously with
* {@link AudioNode#setAudioData(com.jme3.audio.AudioData, com.jme3.audio.AudioKey) }
* or any of the constructors that initialize the audio data.
*/
@@ -312,7 +313,7 @@ public class AudioNode extends Node implements AudioSource {
}
/**
- * @return The {@link Status} of the audio node.
+ * @return The {@link Status} of the audio node.
* The status will be changed when either the {@link AudioNode#play() }
* or {@link AudioNode#stop() } methods are called.
*/
@@ -339,7 +340,7 @@ public class AudioNode extends Node implements AudioSource {
else
return data.getDataType();
}
-
+
/**
* @return True if the audio will keep looping after it is done playing,
* otherwise, false.
@@ -351,7 +352,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the looping mode for the audio node. The default is false.
- *
+ *
* @param loop True if the audio should keep looping after it is done playing.
*/
public void setLooping(boolean loop) {
@@ -362,8 +363,8 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return The pitch of the audio, also the speed of playback.
- *
- * @see AudioNode#setPitch(float)
+ *
+ * @see AudioNode#setPitch(float)
*/
public float getPitch() {
return pitch;
@@ -372,7 +373,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the pitch of the audio, also the speed of playback.
* The value must be between 0.5 and 2.0.
- *
+ *
* @param pitch The pitch to set.
* @throws IllegalArgumentException If pitch is not between 0.5 and 2.0.
*/
@@ -388,7 +389,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return The volume of this audio node.
- *
+ *
* @see AudioNode#setVolume(float)
*/
public float getVolume() {
@@ -397,9 +398,9 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the volume of this audio node.
- *
+ *
* The volume is specified as gain. 1.0 is the default.
- *
+ *
* @param volume The volume to set.
* @throws IllegalArgumentException If volume is negative
*/
@@ -422,7 +423,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the time offset in the sound sample when to start playing.
- *
+ *
* @param timeOffset The time offset
* @throws IllegalArgumentException If timeOffset is negative
*/
@@ -439,7 +440,7 @@ public class AudioNode extends Node implements AudioSource {
play();
}
}
-
+
@Override
public float getPlaybackTime() {
if (channel >= 0)
@@ -451,10 +452,10 @@ public class AudioNode extends Node implements AudioSource {
public Vector3f getPosition() {
return getWorldTranslation();
}
-
+
/**
* @return The velocity of the audio node.
- *
+ *
* @see AudioNode#setVelocity(com.jme3.math.Vector3f)
*/
public Vector3f getVelocity() {
@@ -464,7 +465,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the velocity of the audio node. The velocity is expected
* to be in meters. Does nothing if the audio node is not positional.
- *
+ *
* @param velocity The velocity to set.
* @see AudioNode#setPositional(boolean)
*/
@@ -476,7 +477,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return True if reverb is enabled, otherwise false.
- *
+ *
* @see AudioNode#setReverbEnabled(boolean)
*/
public boolean isReverbEnabled() {
@@ -487,10 +488,10 @@ public class AudioNode extends Node implements AudioSource {
* Set to true to enable reverberation effects for this audio node.
* Does nothing if the audio node is not positional.
*
- * When enabled, the audio environment set with
+ * When enabled, the audio environment set with
* {@link AudioRenderer#setEnvironment(com.jme3.audio.Environment) }
* will apply a reverb effect to the audio playing from this audio node.
- *
+ *
* @param reverbEnabled True to enable reverb.
*/
public void setReverbEnabled(boolean reverbEnabled) {
@@ -502,8 +503,8 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return Filter for the reverberations of this audio node.
- *
- * @see AudioNode#setReverbFilter(com.jme3.audio.Filter)
+ *
+ * @see AudioNode#setReverbFilter(com.jme3.audio.Filter)
*/
public Filter getReverbFilter() {
return reverbFilter;
@@ -515,7 +516,7 @@ public class AudioNode extends Node implements AudioSource {
* The reverb filter will influence the reverberations
* of the audio node playing. This only has an effect if
* reverb is enabled.
- *
+ *
* @param reverbFilter The reverb filter to set.
* @see AudioNode#setDryFilter(com.jme3.audio.Filter)
*/
@@ -527,7 +528,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return Max distance for this audio node.
- *
+ *
* @see AudioNode#setMaxDistance(float)
*/
public float getMaxDistance() {
@@ -545,7 +546,7 @@ public class AudioNode extends Node implements AudioSource {
* get any quieter than at that distance. If you want a sound to fall-off
* very quickly then set ref distance very short and leave this distance
* very long.
- *
+ *
* @param maxDistance The maximum playing distance.
* @throws IllegalArgumentException If maxDistance is negative
*/
@@ -561,8 +562,8 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return The reference playing distance for the audio node.
- *
- * @see AudioNode#setRefDistance(float)
+ *
+ * @see AudioNode#setRefDistance(float)
*/
public float getRefDistance() {
return refDistance;
@@ -574,7 +575,7 @@ public class AudioNode extends Node implements AudioSource {
*
* The reference playing distance is the distance at which the
* audio node will be exactly half of its volume.
- *
+ *
* @param refDistance The reference playing distance.
* @throws IllegalArgumentException If refDistance is negative
*/
@@ -590,8 +591,8 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return True if the audio node is directional
- *
- * @see AudioNode#setDirectional(boolean)
+ *
+ * @see AudioNode#setDirectional(boolean)
*/
public boolean isDirectional() {
return directional;
@@ -601,10 +602,10 @@ public class AudioNode extends Node implements AudioSource {
* Set the audio node to be directional.
* Does nothing if the audio node is not positional.
*
- * After setting directional, you should call
+ * After setting directional, you should call
* {@link AudioNode#setDirection(com.jme3.math.Vector3f) }
* to set the audio node's direction.
- *
+ *
* @param directional If the audio node is directional
*/
public void setDirectional(boolean directional) {
@@ -615,7 +616,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return The direction of this audio node.
- *
+ *
* @see AudioNode#setDirection(com.jme3.math.Vector3f)
*/
public Vector3f getDirection() {
@@ -625,9 +626,9 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the direction of this audio node.
* Does nothing if the audio node is not directional.
- *
- * @param direction
- * @see AudioNode#setDirectional(boolean)
+ *
+ * @param direction
+ * @see AudioNode#setDirectional(boolean)
*/
public void setDirection(Vector3f direction) {
this.direction = direction;
@@ -637,8 +638,8 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return The directional audio node, cone inner angle.
- *
- * @see AudioNode#setInnerAngle(float)
+ *
+ * @see AudioNode#setInnerAngle(float)
*/
public float getInnerAngle() {
return innerAngle;
@@ -647,7 +648,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the directional audio node cone inner angle.
* Does nothing if the audio node is not directional.
- *
+ *
* @param innerAngle The cone inner angle.
*/
public void setInnerAngle(float innerAngle) {
@@ -658,8 +659,8 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return The directional audio node, cone outer angle.
- *
- * @see AudioNode#setOuterAngle(float)
+ *
+ * @see AudioNode#setOuterAngle(float)
*/
public float getOuterAngle() {
return outerAngle;
@@ -668,7 +669,7 @@ public class AudioNode extends Node implements AudioSource {
/**
* Set the directional audio node cone outer angle.
* Does nothing if the audio node is not directional.
- *
+ *
* @param outerAngle The cone outer angle.
*/
public void setOuterAngle(float outerAngle) {
@@ -679,8 +680,8 @@ public class AudioNode extends Node implements AudioSource {
/**
* @return True if the audio node is positional.
- *
- * @see AudioNode#setPositional(boolean)
+ *
+ * @see AudioNode#setPositional(boolean)
*/
public boolean isPositional() {
return positional;
@@ -690,7 +691,7 @@ public class AudioNode extends Node implements AudioSource {
* Set the audio node as positional.
* The position, velocity, and distance parameters effect positional
* audio nodes. Set to false if the audio node should play in "headspace".
- *
+ *
* @param positional True if the audio node should be positional, otherwise
* false if it should be headspace.
*/
@@ -707,7 +708,7 @@ public class AudioNode extends Node implements AudioSource {
if ((refreshFlags & RF_TRANSFORM) != 0){
updatePos = true;
}
-
+
super.updateGeometricState();
if (updatePos && channel >= 0)
@@ -717,13 +718,35 @@ public class AudioNode extends Node implements AudioSource {
@Override
public AudioNode clone(){
AudioNode clone = (AudioNode) super.clone();
-
+
clone.direction = direction.clone();
clone.velocity = velocity.clone();
-
+
return clone;
}
-
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.direction = cloner.clone(direction);
+ this.velocity = cloner.clone(velocity);
+
+ // Change in behavior: the filters were not cloned before meaning
+ // that two cloned audio nodes would share the same filter instance.
+ // While settings will only be applied when the filter is actually
+ // set, I think it's probably surprising to callers if the values of
+ // a filter change from one AudioNode when a different AudioNode's
+ // filter attributes are updated.
+ // Plus if they disable and re-enable the thing using the filter then
+ // the settings get reapplied and it might be surprising to have them
+ // suddenly be strange.
+ // ...so I'll clone them. -pspeed
+ this.dryFilter = cloner.clone(dryFilter);
+ this.reverbFilter = cloner.clone(reverbFilter);
+ }
+
@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
@@ -745,7 +768,7 @@ public class AudioNode extends Node implements AudioSource {
oc.write(direction, "direction", null);
oc.write(innerAngle, "inner_angle", 360);
oc.write(outerAngle, "outer_angle", 360);
-
+
oc.write(positional, "positional", false);
}
@@ -753,7 +776,7 @@ public class AudioNode extends Node implements AudioSource {
public void read(JmeImporter im) throws IOException {
super.read(im);
InputCapsule ic = im.getCapsule(this);
-
+
// NOTE: In previous versions of jME3, audioKey was actually
// written with the name "key". This has been changed
// to "audio_key" in case Spatial's key will be written as "key".
@@ -762,7 +785,7 @@ public class AudioNode extends Node implements AudioSource {
}else{
audioKey = (AudioKey) ic.readSavable("audio_key", null);
}
-
+
loop = ic.readBoolean("looping", false);
volume = ic.readFloat("volume", 1);
pitch = ic.readFloat("pitch", 1);
@@ -779,9 +802,9 @@ public class AudioNode extends Node implements AudioSource {
direction = (Vector3f) ic.readSavable("direction", null);
innerAngle = ic.readFloat("inner_angle", 360);
outerAngle = ic.readFloat("outer_angle", 360);
-
+
positional = ic.readBoolean("positional", false);
-
+
if (audioKey != null) {
try {
data = im.getAssetManager().loadAsset(audioKey);
From 7b29c58fe075ef0127ecb976d96d6e3e22655b64 Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sat, 26 Mar 2016 05:29:22 -0400
Subject: [PATCH 11/77] JmeCloneable related changes to TerrainQuad and
TerrainPatch. Fixed something I missed in NormalRecalcControl.
---
.../geomipmap/NormalRecalcControl.java | 19 ++-
.../jme3/terrain/geomipmap/TerrainPatch.java | 142 +++++++++++-------
.../jme3/terrain/geomipmap/TerrainQuad.java | 101 ++++++++-----
3 files changed, 160 insertions(+), 102 deletions(-)
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 c43c491ec..f810fbf4d 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
@@ -69,12 +69,23 @@ public class NormalRecalcControl extends AbstractControl {
}
- @Override
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
public Object jmeClone() {
NormalRecalcControl control = (NormalRecalcControl)super.jmeClone();
control.setEnabled(true);
- return control;
- }
+ return control;
+ }
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.terrain = cloner.clone(terrain);
+ }
@Override
public Control cloneForSpatial(Spatial spatial) {
@@ -83,7 +94,7 @@ public class NormalRecalcControl extends AbstractControl {
control.setEnabled(true);
return control;
}
-
+
@Override
public void setSpatial(Spatial spatial) {
super.setSpatial(spatial);
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 4ac811e9f..0f6a1bb8d 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
@@ -50,6 +50,7 @@ import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight;
import com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil;
import com.jme3.util.BufferUtils;
+import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.FloatBuffer;
@@ -65,18 +66,18 @@ import java.util.List;
* That uses a geo-mipmapping algorithm to change the index buffer of the mesh.
* The mesh is a triangle strip. In wireframe mode you might notice some strange lines, these are degenerate
* triangles generated by the geoMipMap algorithm and can be ignored. The video card removes them at almost no cost.
- *
+ *
* Each patch needs to know its neighbour's LOD so it can seam its edges with them, in case the neighbour has a different
* LOD. If this doesn't happen, you will see gaps.
- *
+ *
* The LOD value is most detailed at zero. It gets less detailed the higher the LOD value until you reach maxLod, which
* is a mathematical limit on the number of times the 'size' of the patch can be divided by two. However there is a -1 to that
* for now until I add in a custom index buffer calculation for that max level, the current algorithm does not go that far.
- *
- * You can supply a LodThresholdCalculator for use in determining when the LOD should change. It's API will no doubt change
- * in the near future. Right now it defaults to just changing LOD every two patch sizes. So if a patch has a size of 65,
+ *
+ * You can supply a LodThresholdCalculator for use in determining when the LOD should change. It's API will no doubt change
+ * in the near future. Right now it defaults to just changing LOD every two patch sizes. So if a patch has a size of 65,
* then the LOD changes every 130 units away.
- *
+ *
* @author Brent Owens
*/
public class TerrainPatch extends Geometry {
@@ -118,7 +119,7 @@ public class TerrainPatch extends Geometry {
super("TerrainPatch");
setBatchHint(BatchHint.Never);
}
-
+
public TerrainPatch(String name) {
super(name);
setBatchHint(BatchHint.Never);
@@ -221,7 +222,7 @@ public class TerrainPatch extends Geometry {
public FloatBuffer getHeightmap() {
return BufferUtils.createFloatBuffer(geomap.getHeightArray());
}
-
+
public float[] getHeightMap() {
return geomap.getHeightArray();
}
@@ -256,7 +257,7 @@ public class TerrainPatch extends Geometry {
idxB = geomap.writeIndexArrayLodVariable(pow, (int) Math.pow(2, utp.getRightLod()), (int) Math.pow(2, utp.getTopLod()), (int) Math.pow(2, utp.getLeftLod()), (int) Math.pow(2, utp.getBottomLod()), totalSize);
else
idxB = geomap.writeIndexArrayLodDiff(pow, right, top, left, bottom, totalSize);
-
+
Buffer b;
if (idxB.getBuffer() instanceof IntBuffer)
b = (IntBuffer)idxB.getBuffer();
@@ -277,14 +278,14 @@ public class TerrainPatch extends Geometry {
return store.set(getMesh().getFloatBuffer(Type.TexCoord).get(idx*2),
getMesh().getFloatBuffer(Type.TexCoord).get(idx*2+1) );
}
-
+
public float getHeightmapHeight(float x, float z) {
if (x < 0 || z < 0 || x >= size || z >= size)
return 0;
int idx = (int) (z * size + x);
return getMesh().getFloatBuffer(Type.Position).get(idx*3+1); // 3 floats per entry (x,y,z), the +1 is to get the Y
}
-
+
/**
* Get the triangle of this geometry at the specified local coordinate.
* @param x local to the terrain patch
@@ -306,7 +307,7 @@ public class TerrainPatch extends Geometry {
}
protected void setHeight(List locationHeights, boolean overrideHeight) {
-
+
for (LocationHeight lh : locationHeights) {
if (lh.x < 0 || lh.z < 0 || lh.x >= size || lh.z >= size)
continue;
@@ -317,7 +318,7 @@ public class TerrainPatch extends Geometry {
float h = getMesh().getFloatBuffer(Type.Position).get(idx*3+1);
geomap.getHeightArray()[idx] = h+lh.h;
}
-
+
}
FloatBuffer newVertexBuffer = geomap.writeVertexArray(null, stepScale, false);
@@ -351,7 +352,7 @@ public class TerrainPatch extends Geometry {
TB.setUpdateNeeded();
BB.setUpdateNeeded();
}
-
+
/**
* Matches the normals along the edge of the patch with the neighbours.
* Computes the normals for the right, bottom, left, and top edges of the
@@ -364,7 +365,7 @@ public class TerrainPatch extends Geometry {
* *---x---*
* |
* *
- * It works across the right side of the patch, from the top down to
+ * It works across the right side of the patch, from the top down to
* the bottom. Then it works on the bottom side of the patch, from the
* left to the right.
*/
@@ -388,9 +389,9 @@ public class TerrainPatch extends Geometry {
Vector3f binormal = new Vector3f();
Vector3f normal = new Vector3f();
-
+
int s = this.getSize()-1;
-
+
if (right != null) { // right side, works its way down
for (int i=0; i= size || z >= size)
return null; // out of range
-
+
int index = (z*size+x)*3;
FloatBuffer nb = (FloatBuffer)this.getMesh().getBuffer(Type.Normal).getData();
Vector3f normal = new Vector3f();
@@ -609,7 +610,7 @@ public class TerrainPatch extends Geometry {
protected float getHeight(int x, int z, float xm, float zm) {
return geomap.getHeight(x,z,xm,zm);
}
-
+
/**
* Locks the mesh (sets it static) to improve performance.
* But it it not editable then. Set unlock to make it editable.
@@ -626,7 +627,7 @@ public class TerrainPatch extends Geometry {
public void unlockMesh() {
getMesh().setDynamic();
}
-
+
/**
* Returns the offset amount this terrain patch uses for textures.
*
@@ -797,7 +798,7 @@ public class TerrainPatch extends Geometry {
protected void setLodBottom(int lodBottom) {
this.lodBottom = lodBottom;
}
-
+
/*public void setLodCalculator(LodCalculatorFactory lodCalculatorFactory) {
this.lodCalculatorFactory = lodCalculatorFactory;
setLodCalculator(lodCalculatorFactory.createCalculator(this));
@@ -812,7 +813,7 @@ public class TerrainPatch extends Geometry {
if (other instanceof BoundingVolume)
if (!getWorldBound().intersects((BoundingVolume)other))
return 0;
-
+
if(other instanceof Ray)
return collideWithRay((Ray)other, results);
else if (other instanceof BoundingVolume)
@@ -853,7 +854,7 @@ public class TerrainPatch extends Geometry {
* This most definitely is not optimized.
*/
private int collideWithBoundingBox(BoundingBox bbox, CollisionResults results) {
-
+
// test the four corners, for cases where the bbox dimensions are less than the terrain grid size, which is probably most of the time
Vector3f topLeft = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x-bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
Vector3f topRight = worldCoordinateToLocal(new Vector3f(bbox.getCenter().x+bbox.getXExtent(), 0, bbox.getCenter().z-bbox.getZExtent()));
@@ -872,11 +873,11 @@ public class TerrainPatch extends Geometry {
t = getTriangle(bottomRight.x, bottomRight.z);
if (t != null && bbox.collideWith(t, results) > 0)
return 1;
-
+
// box is larger than the points on the terrain, so test against the points
for (float z=topLeft.z; z= size || z >= size)
continue;
t = getTriangle(x,z);
@@ -895,7 +896,7 @@ public class TerrainPatch extends Geometry {
// this reduces the save size to 10% by not saving the mesh
Mesh temp = getMesh();
mesh = null;
-
+
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(size, "size", 16);
@@ -908,7 +909,7 @@ public class TerrainPatch extends Geometry {
//oc.write(lodCalculatorFactory, "lodCalculatorFactory", null);
oc.write(lodEntropy, "lodEntropy", null);
oc.write(geomap, "geomap", null);
-
+
setMesh(temp);
}
@@ -927,7 +928,7 @@ public class TerrainPatch extends Geometry {
//lodCalculatorFactory = (LodCalculatorFactory) ic.readSavable("lodCalculatorFactory", null);
lodEntropy = ic.readFloatArray("lodEntropy", null);
geomap = (LODGeomap) ic.readSavable("geomap", null);
-
+
Mesh regen = geomap.createMesh(stepScale, new Vector2f(1,1), offset, offsetAmount, totalSize, false);
setMesh(regen);
//TangentBinormalGenerator.generate(this); // note that this will be removed
@@ -955,6 +956,33 @@ public class TerrainPatch extends Geometry {
return clone;
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+
+ this.stepScale = cloner.clone(stepScale);
+ this.offset = cloner.clone(offset);
+
+ this.leftNeighbour = null;
+ this.topNeighbour = null;
+ this.rightNeighbour = null;
+ this.bottomNeighbour = null;
+
+ // Don't feel like making geomap cloneable tonight
+ // so I'll copy the old logic.
+ this.geomap = new LODGeomap(size, geomap.getHeightArray());
+ Mesh m = geomap.createMesh(stepScale, Vector2f.UNIT_XY, offset, offsetAmount, totalSize, false);
+ this.setMesh(m);
+
+ // In this case, we always clone material even if the cloner is setup
+ // not to clone it. Terrain uses mutable textures and stuff so it's important
+ // to clone it. (At least that's my understanding and is evidenced by the old
+ // clone code specifically cloning material.) -pspeed
+ this.material = material.clone();
+ }
+
protected void ensurePositiveVolumeBBox() {
if (getModelBound() instanceof BoundingBox) {
if (((BoundingBox)getModelBound()).getYExtent() < 0.001f) {
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 8cceb85bb..2553e06a0 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
@@ -55,6 +55,7 @@ import com.jme3.terrain.geomipmap.picking.BresenhamTerrainPicker;
import com.jme3.terrain.geomipmap.picking.TerrainPickData;
import com.jme3.terrain.geomipmap.picking.TerrainPicker;
import com.jme3.util.TangentBinormalGenerator;
+import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -126,7 +127,7 @@ public class TerrainQuad extends Node implements Terrain {
private Vector3f lastScale = Vector3f.UNIT_XYZ;
protected NeighbourFinder neighbourFinder;
-
+
public TerrainQuad() {
super("Terrain");
}
@@ -144,24 +145,24 @@ public class TerrainQuad extends Node implements Terrain {
*
* @param name the name of the scene element. This is required for
* identification and comparison purposes.
- * @param patchSize size of the individual patches (geometry). Power of 2 plus 1,
+ * @param patchSize size of the individual patches (geometry). Power of 2 plus 1,
* must be smaller than totalSize. (eg. 33, 65...)
- * @param totalSize the size of this entire terrain (on one side). Power of 2 plus 1
+ * @param totalSize the size of this entire terrain (on one side). Power of 2 plus 1
* (eg. 513, 1025, 2049...)
* @param heightMap The height map to generate the terrain from (a flat
- * height map will be generated if this is null). The size of one side of the heightmap
+ * height map will be generated if this is null). The size of one side of the heightmap
* must match the totalSize. So a 513x513 heightmap is needed for a terrain with totalSize of 513.
*/
public TerrainQuad(String name, int patchSize, int totalSize, float[] heightMap) {
this(name, patchSize, totalSize, Vector3f.UNIT_XYZ, heightMap);
-
+
affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), size*2, Float.MAX_VALUE, size*2);
fixNormalEdges(affectedAreaBBox);
addControl(new NormalRecalcControl(this));
}
-
+
/**
- *
+ *
* @param name the name of the scene element. This is required for
* identification and comparison purposes.
* @param patchSize size of the individual patches
@@ -176,7 +177,7 @@ public class TerrainQuad extends Node implements Terrain {
}
/**
- *
+ *
* @param name the name of the scene element. This is required for
* identification and comparison purposes.
* @param patchSize size of the individual patches
@@ -192,9 +193,9 @@ public class TerrainQuad extends Node implements Terrain {
//fixNormalEdges(affectedAreaBBox);
//addControl(new NormalRecalcControl(this));
}
-
+
/**
- *
+ *
* @param name the name of the scene element. This is required for
* identification and comparison purposes.
* @param patchSize size of the individual patches
@@ -217,17 +218,17 @@ public class TerrainQuad extends Node implements Terrain {
Vector2f offset, float offsetAmount)
{
super(name);
-
+
if (heightMap == null)
heightMap = generateDefaultHeightMap(quadSize);
-
+
if (!FastMath.isPowerOfTwo(quadSize - 1)) {
throw new RuntimeException("size given: " + quadSize + " Terrain quad sizes may only be (2^N + 1)");
}
if (FastMath.sqrt(heightMap.length) > quadSize) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Heightmap size is larger than the terrain size. Make sure your heightmap image is the same size as the terrain!");
}
-
+
this.offset = offset;
this.offsetAmount = offsetAmount;
this.totalSize = totalSize;
@@ -248,7 +249,7 @@ public class TerrainQuad extends Node implements Terrain {
public void recalculateAllNormals() {
affectedAreaBBox = new BoundingBox(new Vector3f(0,0,0), totalSize*2, Float.MAX_VALUE, totalSize*2);
}
-
+
/**
* Create just a flat heightmap
*/
@@ -267,11 +268,11 @@ public class TerrainQuad extends Node implements Terrain {
//TODO background-thread this if it ends up being expensive
fixNormals(affectedAreaBBox); // the affected patches
fixNormalEdges(affectedAreaBBox); // the edges between the patches
-
+
setNormalRecalcNeeded(null); // set to false
}
}
-
+
/**
* Caches the transforms (except rotation) so the LOD calculator,
* which runs on a separate thread, can access them safely.
@@ -343,7 +344,7 @@ public class TerrainQuad extends Node implements Terrain {
public Material getMaterial() {
return getMaterial(null);
}
-
+
public Material getMaterial(Vector3f worldLocation) {
// get the material from one of the children. They all share the same material
if (children != null) {
@@ -362,7 +363,7 @@ public class TerrainQuad extends Node implements Terrain {
public int getNumMajorSubdivisions() {
return 1;
}
-
+
protected boolean calculateLod(List location, HashMap updates, LodCalculator lodCalculator) {
@@ -434,7 +435,7 @@ public class TerrainQuad extends Node implements Terrain {
utp.setBottomLod(utpD.getNewLod());
utpD.setTopLod(utp.getNewLod());
}
-
+
if (left != null) {
UpdatedTerrainPatch utpL = updated.get(left.getName());
if (utpL == null) {
@@ -478,7 +479,7 @@ public class TerrainQuad extends Node implements Terrain {
}
}
}
-
+
/**
* Find any neighbours that should have their edges seamed because another neighbour
* changed its LOD to a greater value (less detailed)
@@ -587,10 +588,10 @@ public class TerrainQuad extends Node implements Terrain {
/**
* Quadrants, world coordinates, and heightmap coordinates (Y-up):
- *
+ *
* -z
- * -u |
- * -v 1|3
+ * -u |
+ * -v 1|3
* -x ----+---- x
* 2|4 u
* | v
@@ -668,7 +669,7 @@ public class TerrainQuad extends Node implements Terrain {
quad3.setLocalTranslation(origin3);
quad3.quadrant = 3;
this.attachChild(quad3);
-
+
// 4 lower right of heightmap, lower right quad
float[] heightBlock4 = createHeightSubBlock(heightMap, split - 1,
split - 1, split);
@@ -892,7 +893,7 @@ public class TerrainQuad extends Node implements Terrain {
}
return false;
}
-
+
/**
* This will cause all normals for this terrain quad to be recalculated
*/
@@ -1024,14 +1025,14 @@ public class TerrainQuad extends Node implements Terrain {
int col;
int row;
Spatial child;
-
+
QuadrantChild(int col, int row, Spatial child) {
this.col = col;
this.row = row;
this.child = child;
}
}
-
+
private QuadrantChild findMatchingChild(int x, int z) {
int quad = findQuadrant(x, z);
int split = (size + 1) >> 1;
@@ -1069,7 +1070,7 @@ public class TerrainQuad extends Node implements Terrain {
}
return null;
}
-
+
/**
* Get the interpolated height of the terrain at the specified point.
* @param xz the location to get the height for
@@ -1090,7 +1091,7 @@ public class TerrainQuad extends Node implements Terrain {
* gets an interpolated value at the specified point
*/
protected float getHeight(int x, int z, float xm, float zm) {
-
+
QuadrantChild match = findMatchingChild(x,z);
if (match != null) {
if (match.child instanceof TerrainQuad) {
@@ -1107,10 +1108,10 @@ public class TerrainQuad extends Node implements Terrain {
float x = (float)(((xz.x - getWorldTranslation().x) / getWorldScale().x) + (float)(totalSize-1) / 2f);
float z = (float)(((xz.y - getWorldTranslation().z) / getWorldScale().z) + (float)(totalSize-1) / 2f);
Vector3f normal = getNormal(x, z, xz);
-
+
return normal;
}
-
+
protected Vector3f getNormal(float x, float z, Vector2f xz) {
x-=0.5f;
z-=0.5f;
@@ -1125,15 +1126,15 @@ public class TerrainQuad extends Node implements Terrain {
// v3--v4 | Z
// |
// <-------Y
- // X
+ // X
Vector3f n1 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.ceil(z));
Vector3f n2 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.ceil(z));
Vector3f n3 = getMeshNormal((int) FastMath.ceil(x), (int) FastMath.floor(z));
Vector3f n4 = getMeshNormal((int) FastMath.floor(x), (int) FastMath.floor(z));
-
+
return n1.add(n2).add(n3).add(n4).normalize();
}
-
+
public void setHeight(Vector2f xz, float height) {
List coord = new ArrayList();
coord.add(xz);
@@ -1291,7 +1292,7 @@ public class TerrainQuad extends Node implements Terrain {
return (x >= 0 && x <= totalSize && z >= 0 && z <= totalSize);
}
-
+
public int getTerrainSize() {
return totalSize;
}
@@ -1750,7 +1751,7 @@ public class TerrainQuad extends Node implements Terrain {
totalSize = c.readInt("totalSize", 0);
//lodCalculator = (LodCalculator) c.readSavable("lodCalculator", createDefaultLodCalculator());
//lodCalculatorFactory = (LodCalculatorFactory) c.readSavable("lodCalculatorFactory", null);
-
+
if ( !(getParent() instanceof TerrainQuad) ) {
BoundingBox all = new BoundingBox(getWorldTranslation(), totalSize, totalSize, totalSize);
affectedAreaBBox = all;
@@ -1793,10 +1794,10 @@ public class TerrainQuad extends Node implements Terrain {
quadClone.quadrant = quadrant;
//quadClone.lodCalculatorFactory = lodCalculatorFactory.clone();
//quadClone.lodCalculator = lodCalculator.clone();
-
+
TerrainLodControl lodControlCloned = this.getControl(TerrainLodControl.class);
TerrainLodControl lodControl = quadClone.getControl(TerrainLodControl.class);
-
+
if (lodControlCloned != null && !(getParent() instanceof TerrainQuad)) {
//lodControlCloned.setLodCalculator(lodControl.getLodCalculator().clone());
}
@@ -1806,7 +1807,25 @@ public class TerrainQuad extends Node implements Terrain {
return quadClone;
}
-
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.stepScale = cloner.clone(stepScale);
+ this.offset = cloner.clone(offset);
+
+ // This was not cloned before... I think that's a mistake.
+ this.affectedAreaBBox = cloner.clone(affectedAreaBBox);
+
+ // picker is not cloneable and not cloned. This also seems like
+ // a mistake if you ever load the same terrain twice.
+ // this.picker = cloner.clone(picker);
+
+ // neighbourFinder is also not cloned. Maybe that's ok.
+ }
+
@Override
protected void setParent(Node parent) {
super.setParent(parent);
@@ -1815,7 +1834,7 @@ public class TerrainQuad extends Node implements Terrain {
clearCaches();
}
}
-
+
/**
* Removes any cached references this terrain is holding, in particular
* the TerrainPatch's neighbour references.
@@ -1834,7 +1853,7 @@ public class TerrainQuad extends Node implements Terrain {
}
}
}
-
+
public int getMaxLod() {
if (maxLod < 0)
maxLod = Math.max(1, (int) (FastMath.log(size-1)/FastMath.log(2)) -1); // -1 forces our minimum of 4 triangles wide
From c6aac78f426886b7e71e7cc819678c6deae5ed8e Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sun, 27 Mar 2016 04:51:31 -0400
Subject: [PATCH 12/77] Added a clone() method and implement Cloneable. Removed
whitespace from the ends of lines.
---
.../java/com/jme3/util/SafeArrayList.java | 164 ++++++++++--------
1 file changed, 91 insertions(+), 73 deletions(-)
diff --git a/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java b/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java
index 27f129f2f..a0657c753 100644
--- a/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java
+++ b/jme3-core/src/main/java/com/jme3/util/SafeArrayList.java
@@ -43,7 +43,7 @@ import java.util.*;
* the list is changing.
*
*
All modifications, including set() operations will cause a copy of the
- * data to be created that replaces the old version. Because this list is
+ * data to be created that replaces the old version. Because this list is
* not designed for threading concurrency it further optimizes the "many modifications"
* case by buffering them as a normal ArrayList until the next time the contents
* are accessed.
@@ -63,16 +63,16 @@ import java.util.*;
* Even after ListIterator.remove() or Iterator.remove() is called, this change
* is not reflected in the iterator instance as it is still refering to its
* original snapshot.
- *
+ *
*
* @version $Revision$
* @author Paul Speed
*/
-public class SafeArrayList implements List {
-
+public class SafeArrayList implements List, Cloneable {
+
// Implementing List directly to avoid accidentally acquiring
// incorrect or non-optimal behavior from AbstractList. For
- // example, the default iterator() method will not work for
+ // example, the default iterator() method will not work for
// this list.
// Note: given the particular use-cases this was intended,
@@ -81,30 +81,48 @@ public class SafeArrayList implements List {
// SafeArrayList-specific methods could then be exposed
// for the classes like Node and Spatial to use to manage
// the list. This was the callers couldn't remove a child
- // without it being detached properly, for example.
+ // without it being detached properly, for example.
- private Class elementType;
+ private Class elementType;
private List buffer;
private E[] backingArray;
private int size = 0;
-
+
public SafeArrayList(Class elementType) {
- this.elementType = elementType;
+ this.elementType = elementType;
}
-
+
public SafeArrayList(Class elementType, Collection extends E> c) {
- this.elementType = elementType;
+ this.elementType = elementType;
addAll(c);
}
+ public SafeArrayList clone() {
+ try {
+ SafeArrayList clone = (SafeArrayList)super.clone();
+
+ // Clone whichever backing store is currently active
+ if( backingArray != null ) {
+ clone.backingArray = backingArray.clone();
+ }
+ if( buffer != null ) {
+ clone.buffer = (List)((ArrayList)buffer).clone();
+ }
+
+ return clone;
+ } catch( CloneNotSupportedException e ) {
+ throw new AssertionError();
+ }
+ }
+
protected final T[] createArray(Class type, int size) {
- return (T[])java.lang.reflect.Array.newInstance(type, size);
+ return (T[])java.lang.reflect.Array.newInstance(type, size);
}
-
+
protected final E[] createArray(int size) {
- return createArray(elementType, size);
+ return createArray(elementType, size);
}
-
+
/**
* Returns a current snapshot of this List's backing array that
* is guaranteed not to change through further List manipulation.
@@ -114,10 +132,10 @@ public class SafeArrayList implements List {
public final E[] getArray() {
if( backingArray != null )
return backingArray;
-
+
if( buffer == null ) {
backingArray = createArray(0);
- } else {
+ } else {
// Only keep the array or the buffer but never both at
// the same time. 1) it saves space, 2) it keeps the rest
// of the code safer.
@@ -126,35 +144,35 @@ public class SafeArrayList implements List {
}
return backingArray;
}
-
+
protected final List getBuffer() {
if( buffer != null )
return buffer;
-
+
if( backingArray == null ) {
buffer = new ArrayList();
- } else {
+ } else {
// Only keep the array or the buffer but never both at
// the same time. 1) it saves space, 2) it keeps the rest
- // of the code safer.
+ // of the code safer.
buffer = new ArrayList( Arrays.asList(backingArray) );
backingArray = null;
}
return buffer;
}
-
+
public final int size() {
- return size;
+ return size;
}
-
+
public final boolean isEmpty() {
return size == 0;
}
-
+
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
-
+
public Iterator iterator() {
return listIterator();
}
@@ -162,70 +180,70 @@ public class SafeArrayList implements List {
public Object[] toArray() {
return getArray();
}
-
+
public T[] toArray(T[] a) {
-
+
E[] array = getArray();
if (a.length < array.length) {
return (T[])Arrays.copyOf(array, array.length, a.getClass());
- }
-
+ }
+
System.arraycopy( array, 0, a, 0, array.length );
-
+
if (a.length > array.length) {
a[array.length] = null;
}
-
+
return a;
}
-
+
public boolean add(E e) {
boolean result = getBuffer().add(e);
size = getBuffer().size();
return result;
}
-
+
public boolean remove(Object o) {
boolean result = getBuffer().remove(o);
size = getBuffer().size();
return result;
}
-
+
public boolean containsAll(Collection> c) {
return Arrays.asList(getArray()).containsAll(c);
}
-
+
public boolean addAll(Collection extends E> c) {
boolean result = getBuffer().addAll(c);
size = getBuffer().size();
return result;
}
-
+
public boolean addAll(int index, Collection extends E> c) {
boolean result = getBuffer().addAll(index, c);
size = getBuffer().size();
return result;
}
-
+
public boolean removeAll(Collection> c) {
boolean result = getBuffer().removeAll(c);
size = getBuffer().size();
return result;
}
-
+
public boolean retainAll(Collection> c) {
boolean result = getBuffer().retainAll(c);
size = getBuffer().size();
return result;
}
-
+
public void clear() {
getBuffer().clear();
size = 0;
}
-
+
public boolean equals(Object o) {
- if( o == this )
+ if( o == this )
return true;
if( !(o instanceof List) ) //covers null too
return false;
@@ -240,9 +258,9 @@ public class SafeArrayList implements List {
if( o1 == null || !o1.equals(o2) )
return false;
}
- return !(i1.hasNext() || !i2.hasNext());
+ return !(i1.hasNext() || !i2.hasNext());
}
-
+
public int hashCode() {
// Exactly the hash code described in the List interface, basically
E[] array = getArray();
@@ -252,30 +270,30 @@ public class SafeArrayList implements List {
}
return result;
}
-
+
public final E get(int index) {
if( backingArray != null )
return backingArray[index];
if( buffer != null )
return buffer.get(index);
- throw new IndexOutOfBoundsException( "Index:" + index + ", Size:0" );
+ throw new IndexOutOfBoundsException( "Index:" + index + ", Size:0" );
}
-
+
public E set(int index, E element) {
return getBuffer().set(index, element);
}
-
+
public void add(int index, E element) {
getBuffer().add(index, element);
size = getBuffer().size();
}
-
+
public E remove(int index) {
E result = getBuffer().remove(index);
size = getBuffer().size();
return result;
}
-
+
public int indexOf(Object o) {
E[] array = getArray();
for( int i = 0; i < array.length; i++ ) {
@@ -289,7 +307,7 @@ public class SafeArrayList implements List {
}
return -1;
}
-
+
public int lastIndexOf(Object o) {
E[] array = getArray();
for( int i = array.length - 1; i >= 0; i-- ) {
@@ -303,29 +321,29 @@ public class SafeArrayList implements List {
}
return -1;
}
-
+
public ListIterator listIterator() {
return new ArrayIterator(getArray(), 0);
}
-
+
public ListIterator listIterator(int index) {
return new ArrayIterator(getArray(), index);
}
-
+
public List subList(int fromIndex, int toIndex) {
-
+
// So far JME doesn't use subList that I can see so I'm nerfing it.
List raw = Arrays.asList(getArray()).subList(fromIndex, toIndex);
return Collections.unmodifiableList(raw);
}
-
+
public String toString() {
-
+
E[] array = getArray();
if( array.length == 0 ) {
return "[]";
}
-
+
StringBuilder sb = new StringBuilder();
sb.append('[');
for( int i = 0; i < array.length; i++ ) {
@@ -337,63 +355,63 @@ public class SafeArrayList implements List {
sb.append(']');
return sb.toString();
}
-
+
protected class ArrayIterator implements ListIterator {
private E[] array;
private int next;
private int lastReturned;
-
+
protected ArrayIterator( E[] array, int index ) {
this.array = array;
this.next = index;
this.lastReturned = -1;
}
-
+
public boolean hasNext() {
return next != array.length;
}
-
+
public E next() {
if( !hasNext() )
throw new NoSuchElementException();
lastReturned = next++;
return array[lastReturned];
}
-
+
public boolean hasPrevious() {
- return next != 0;
- }
-
+ return next != 0;
+ }
+
public E previous() {
if( !hasPrevious() )
throw new NoSuchElementException();
lastReturned = --next;
return array[lastReturned];
}
-
+
public int nextIndex() {
- return next;
+ return next;
}
-
+
public int previousIndex() {
return next - 1;
}
-
+
public void remove() {
// This operation is not so easy to do but we will fake it.
// The issue is that the backing list could be completely
// different than the one this iterator is a snapshot of.
- // We'll just remove(element) which in most cases will be
+ // We'll just remove(element) which in most cases will be
// correct. If the list had earlier .equals() equivalent
// elements then we'll remove one of those instead. Either
// way, none of those changes are reflected in this iterator.
SafeArrayList.this.remove( array[lastReturned] );
}
-
+
public void set(E e) {
throw new UnsupportedOperationException();
}
-
+
public void add(E e) {
throw new UnsupportedOperationException();
}
From 2028f3b3f864884d03d4ee9bf2b86f87688ee691 Mon Sep 17 00:00:00 2001
From: Paul Speed
Date: Sun, 27 Mar 2016 04:53:12 -0400
Subject: [PATCH 13/77] Added 'finer' logging for the clone() method to provide
visibility for debugging. Added a setClonedValue() method to force uncloned
or precloned references in some specific use-cases. Added an isCloned()
method to tell if an object has already been cloned in this cloner's
'session'.
---
.../main/java/com/jme3/util/clone/Cloner.java | 190 +++++++++++-------
1 file changed, 119 insertions(+), 71 deletions(-)
diff --git a/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java b/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java
index cc046b28a..be34755be 100644
--- a/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java
+++ b/jme3-core/src/main/java/com/jme3/util/clone/Cloner.java
@@ -37,6 +37,8 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.logging.Logger;
+import java.util.logging.Level;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -49,7 +51,7 @@ import java.util.concurrent.ConcurrentHashMap;
*
By default, objects that do not implement JmeCloneable will
* be treated like normal Java Cloneable objects. If the object does
* not implement the JmeCloneable or the regular JDK Cloneable interfaces
- * AND has no special handling defined then an IllegalArgumentException
+ * AND has no special handling defined then an IllegalArgumentException
* will be thrown.
*
*
Enhanced object cloning is done in a two step process. First,
@@ -60,7 +62,7 @@ import java.util.concurrent.ConcurrentHashMap;
* can easily have a regular shallow clone implementation just like any
* normal Java objects. Second, the deep cloning of fields happens after
* creation wich means that the clone is available to future field cloning
- * to resolve circular references.
+ * to resolve circular references.
*
*
Similar to Java serialization, the handling of specific object
* types can be customized. This allows certain objects to be cloned gracefully
@@ -87,31 +89,33 @@ import java.util.concurrent.ConcurrentHashMap;
* Foo fooClone = cloner.clone(foo);
* cloner.clearIndex(); // prepare it for reuse
* Foo fooClone2 = cloner.clone(foo);
- *
+ *
* // Example 2: using the utility method that self-instantiates a temporary cloner.
* Foo fooClone = Cloner.deepClone(foo);
- *
+ *
*
*
* @author Paul Speed
*/
public class Cloner {
-
+
+ static Logger log = Logger.getLogger(Cloner.class.getName());
+
/**
* Keeps track of the objects that have been cloned so far.
- */
+ */
private IdentityHashMap