LightList
is used internally by {@link Spatial}s to manage
* lights that are attached to them.
- *
+ *
* @author Kirill Vainer
*/
-public final class LightList implements IterableLightList
for the given {@link Spatial}.
- *
+ *
* @param owner The spatial owner
*/
public LightList(Spatial owner) {
@@ -87,7 +89,7 @@ public final class LightList implements IterableGeometry
* is managed by.
*/
protected GeometryGroupNode groupNode;
-
+
/**
- * The start index of this Geometry's
inside
+ * The start index of this Geometry's
inside
* the {@link GeometryGroupNode}.
*/
protected int startIndex = -1;
-
+
/**
* Serialization only. Do not use.
*/
@@ -95,37 +96,37 @@ public class Geometry extends Spatial {
* Create a geometry node without any mesh data.
* Both the mesh and the material are null, the geometry
* cannot be rendered until those are set.
- *
+ *
* @param name The name of this geometry
*/
public Geometry(String name) {
super(name);
-
+
// For backwards compatibility, only clear the "requires
// update" flag if we are not a subclass of Geometry.
// This prevents subclass from silently failing to receive
// updates when they upgrade.
- setRequiresUpdates(Geometry.class != getClass());
+ setRequiresUpdates(Geometry.class != getClass());
}
/**
* Create a geometry node with mesh data.
* The material of the geometry is null, it cannot
* be rendered until it is set.
- *
+ *
* @param name The name of this geometry
* @param mesh The mesh data for this geometry
*/
public Geometry(String name, Mesh mesh) {
this(name);
-
+
if (mesh == null) {
throw new IllegalArgumentException("mesh cannot be null");
}
this.mesh = mesh;
}
-
+
@Override
public boolean checkCulling(Camera cam) {
if (isGrouped()) {
@@ -137,8 +138,8 @@ public class Geometry extends Spatial {
/**
* @return If ignoreTransform mode is set.
- *
- * @see Geometry#setIgnoreTransform(boolean)
+ *
+ * @see Geometry#setIgnoreTransform(boolean)
*/
public boolean isIgnoreTransform() {
return ignoreTransform;
@@ -156,7 +157,7 @@ public class Geometry extends Spatial {
* Level 0 indicates that the default index buffer should be used,
* levels [1, LodLevels + 1] represent the levels set on the mesh
* with {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }.
- *
+ *
* @param lod The lod level to set
*/
@Override
@@ -170,7 +171,7 @@ public class Geometry extends Spatial {
}
lodLevel = lod;
-
+
if (isGrouped()) {
groupNode.onMeshChange(this);
}
@@ -178,7 +179,7 @@ public class Geometry extends Spatial {
/**
* Returns the LOD level set with {@link #setLodLevel(int) }.
- *
+ *
* @return the LOD level set
*/
public int getLodLevel() {
@@ -187,10 +188,10 @@ public class Geometry extends Spatial {
/**
* Returns this geometry's mesh vertex count.
- *
+ *
* @return this geometry's mesh vertex count.
- *
- * @see Mesh#getVertexCount()
+ *
+ * @see Mesh#getVertexCount()
*/
public int getVertexCount() {
return mesh.getVertexCount();
@@ -198,10 +199,10 @@ public class Geometry extends Spatial {
/**
* Returns this geometry's mesh triangle count.
- *
+ *
* @return this geometry's mesh triangle count.
- *
- * @see Mesh#getTriangleCount()
+ *
+ * @see Mesh#getTriangleCount()
*/
public int getTriangleCount() {
return mesh.getTriangleCount();
@@ -209,9 +210,9 @@ public class Geometry extends Spatial {
/**
* Sets the mesh to use for this geometry when rendering.
- *
+ *
* @param mesh the mesh to use for this geometry
- *
+ *
* @throws IllegalArgumentException If mesh is null
*/
public void setMesh(Mesh mesh) {
@@ -221,7 +222,7 @@ public class Geometry extends Spatial {
this.mesh = mesh;
setBoundRefresh();
-
+
if (isGrouped()) {
groupNode.onMeshChange(this);
}
@@ -229,10 +230,10 @@ public class Geometry extends Spatial {
/**
* Returns the mesh to use for this geometry
- *
+ *
* @return the mesh to use for this geometry
- *
- * @see #setMesh(com.jme3.scene.Mesh)
+ *
+ * @see #setMesh(com.jme3.scene.Mesh)
*/
public Mesh getMesh() {
return mesh;
@@ -240,13 +241,13 @@ public class Geometry extends Spatial {
/**
* Sets the material to use for this geometry.
- *
+ *
* @param material the material to use for this geometry
*/
@Override
public void setMaterial(Material material) {
this.material = material;
-
+
if (isGrouped()) {
groupNode.onMaterialChange(this);
}
@@ -254,10 +255,10 @@ public class Geometry extends Spatial {
/**
* Returns the material that is used for this geometry.
- *
+ *
* @return the material that is used for this geometry
- *
- * @see #setMaterial(com.jme3.material.Material)
+ *
+ * @see #setMaterial(com.jme3.material.Material)
*/
public Material getMaterial() {
return material;
@@ -310,18 +311,18 @@ public class Geometry extends Spatial {
computeWorldMatrix();
if (isGrouped()) {
- groupNode.onTransformChange(this);
+ groupNode.onTransformChange(this);
}
-
+
// geometry requires lights to be sorted
worldLights.sort(true);
}
/**
* Associate this Geometry
with a {@link GeometryGroupNode}.
- *
+ *
* Should only be called by the parent {@link GeometryGroupNode}.
- *
+ *
* @param node Which {@link GeometryGroupNode} to associate with.
* @param startIndex The starting index of this geometry in the group.
*/
@@ -329,26 +330,26 @@ public class Geometry extends Spatial {
if (isGrouped()) {
unassociateFromGroupNode();
}
-
+
this.groupNode = node;
this.startIndex = startIndex;
}
/**
- * Removes the {@link GeometryGroupNode} association from this
+ * Removes the {@link GeometryGroupNode} association from this
* Geometry
.
- *
+ *
* Should only be called by the parent {@link GeometryGroupNode}.
*/
public void unassociateFromGroupNode() {
if (groupNode != null) {
- // Once the geometry is removed
+ // Once the geometry is removed
// from the parent, the group node needs to be updated.
groupNode.onGeometryUnassociated(this);
groupNode = null;
-
+
// change the default to -1 to make error detection easier
- startIndex = -1;
+ startIndex = -1;
}
}
@@ -360,7 +361,7 @@ public class Geometry extends Spatial {
@Override
protected void setParent(Node parent) {
super.setParent(parent);
-
+
// If the geometry is managed by group node we need to unassociate.
if (parent == null && isGrouped()) {
unassociateFromGroupNode();
@@ -406,7 +407,7 @@ public class Geometry extends Spatial {
* {@link Geometry#getWorldTransform() world transform} of this geometry.
* In order to receive updated values, you must call {@link Geometry#computeWorldMatrix() }
* before using this method.
- *
+ *
* @return Matrix to transform from local space to world space
*/
public Matrix4f getWorldMatrix() {
@@ -418,7 +419,7 @@ public class Geometry extends Spatial {
* This alters the bound used on the mesh as well via
* {@link Mesh#setBound(com.jme3.bounding.BoundingVolume) } and
* forces the world bounding volume to be recomputed.
- *
+ *
* @param modelBound The model bound to set
*/
@Override
@@ -465,15 +466,15 @@ public class Geometry extends Spatial {
}
/**
- * Determine whether this Geometry
is managed by a
+ * Determine whether this Geometry
is managed by a
* {@link GeometryGroupNode} or not.
- *
+ *
* @return True if managed by a {@link GeometryGroupNode}.
*/
public boolean isGrouped() {
return groupNode != null;
}
-
+
/**
* @deprecated Use {@link #isGrouped()} instead.
*/
@@ -492,14 +493,14 @@ public class Geometry extends Spatial {
@Override
public Geometry clone(boolean cloneMaterial) {
Geometry geomClone = (Geometry) super.clone(cloneMaterial);
-
+
// This geometry is managed,
// but the cloned one is not attached to anything, hence not managed.
if (geomClone.isGrouped()) {
geomClone.groupNode = null;
geomClone.startIndex = -1;
}
-
+
geomClone.cachedWorldMat = cachedWorldMat.clone();
if (material != null) {
if (cloneMaterial) {
@@ -539,6 +540,16 @@ public class Geometry extends Spatial {
return geomClone;
}
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ this.mesh = cloner.clone(mesh);
+ this.material = cloner.clone(material);
+ this.groupNode = cloner.clone(groupNode);
+ }
+
@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
diff --git a/jme3-core/src/main/java/com/jme3/scene/LightNode.java b/jme3-core/src/main/java/com/jme3/scene/LightNode.java
index fea63bf57..1a87d11b4 100644
--- a/jme3-core/src/main/java/com/jme3/scene/LightNode.java
+++ b/jme3-core/src/main/java/com/jme3/scene/LightNode.java
@@ -36,11 +36,12 @@ import com.jme3.export.JmeImporter;
import com.jme3.light.Light;
import com.jme3.scene.control.LightControl;
import com.jme3.scene.control.LightControl.ControlDirection;
+import com.jme3.util.clone.Cloner;
import java.io.IOException;
/**
* LightNode
is used to link together a {@link Light} object
- * with a {@link Node} object.
+ * with a {@link Node} object.
*
* @author Tim8Dev
*/
@@ -66,7 +67,7 @@ public class LightNode extends Node {
/**
* Enable or disable the LightNode
functionality.
- *
+ *
* @param enabled If false, the functionality of LightNode will
* be disabled.
*/
@@ -93,7 +94,18 @@ public class LightNode extends Node {
public Light getLight() {
return lightControl.getLight();
}
-
+
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public void cloneFields( Cloner cloner, Object original ) {
+ // A change in behavior... I think previously LightNode was probably
+ // not really cloneable... or at least its lightControl would be pointing
+ // to the wrong control. -pspeed
+ this.lightControl = cloner.clone(lightControl);
+ }
+
@Override
public void read(JmeImporter im) throws IOException {
super.read(im);
diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java
index 60f7eabe2..f9b66bc45 100644
--- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java
+++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java
@@ -51,6 +51,8 @@ import com.jme3.util.BufferUtils;
import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry;
import com.jme3.util.SafeArrayList;
+import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
import java.nio.*;
import java.util.ArrayList;
@@ -61,7 +63,7 @@ import java.util.ArrayList;
* All visible elements in a scene are represented by meshes.
* Meshes may contain three types of geometric primitives:
* gl_PointSize
.
- *
+ *
* @param pointSize The size of points
*/
public void setPointSize(float pointSize) {
@@ -582,7 +613,7 @@ public class Mesh implements Savable, Cloneable {
/**
* Returns the line width for line meshes.
- *
+ *
* @return the line width
* @deprecated use {@link Material#getAdditionalRenderState()} and {@link RenderState#getLineWidth()}
*/
@@ -593,9 +624,9 @@ public class Mesh implements Savable, Cloneable {
/**
* Specify the line width for meshes of the line modes, such
- * as {@link Mode#Lines}. The line width is specified as on-screen pixels,
+ * as {@link Mode#Lines}. The line width is specified as on-screen pixels,
* the default value is 1.0.
- *
+ *
* @param lineWidth The line width
* @deprecated use {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)}
*/
@@ -605,7 +636,7 @@ public class Mesh implements Savable, Cloneable {
}
/**
- * Indicates to the GPU that this mesh will not be modified (a hint).
+ * Indicates to the GPU that this mesh will not be modified (a hint).
* Sets the usage mode to {@link Usage#Static}
* for all {@link VertexBuffer vertex buffers} on this Mesh.
*/
@@ -646,7 +677,7 @@ public class Mesh implements Savable, Cloneable {
public void setInterleaved(){
ArrayListindex
argument.
- *
- * @param index The index of the triangle.
+ *
+ * @param index The index of the triangle.
* Should be between 0 and {@link #getTriangleCount()}.
- *
+ *
* @param tri The triangle to store the positions in
*/
public void getTriangle(int index, Triangle tri){
@@ -886,12 +917,12 @@ public class Mesh implements Savable, Cloneable {
}
/**
- * Gets the triangle vertex indices at the given triangle index
+ * Gets the triangle vertex indices at the given triangle index
* and stores them into the given int array.
- *
- * @param index The index of the triangle.
+ *
+ * @param index The index of the triangle.
* Should be between 0 and {@link #getTriangleCount()}.
- *
+ *
* @param indices Indices of the triangle's vertices
*/
public void getTriangle(int index, int[] indices){
@@ -917,15 +948,15 @@ public class Mesh implements Savable, Cloneable {
public void setId(int id){
if (vertexArrayID != -1)
throw new IllegalStateException("ID has already been set.");
-
+
vertexArrayID = id;
}
/**
* Generates a collision tree for the mesh.
- * Called automatically by {@link #collideWith(com.jme3.collision.Collidable,
- * com.jme3.math.Matrix4f,
- * com.jme3.bounding.BoundingVolume,
+ * Called automatically by {@link #collideWith(com.jme3.collision.Collidable,
+ * com.jme3.math.Matrix4f,
+ * com.jme3.bounding.BoundingVolume,
* com.jme3.collision.CollisionResults) }.
*/
public void createCollisionData(){
@@ -948,7 +979,7 @@ public class Mesh implements Savable, Cloneable {
* User code should only use collideWith() on scene
* graph elements such as {@link Spatial}s.
*/
- public int collideWith(Collidable other,
+ public int collideWith(Collidable other,
Matrix4f worldMatrix,
BoundingVolume worldBound,
CollisionResults results){
@@ -956,18 +987,18 @@ public class Mesh implements Savable, Cloneable {
if (getVertexCount() == 0) {
return 0;
}
-
+
if (collisionTree == null){
createCollisionData();
}
-
+
return collisionTree.collideWith(other, worldMatrix, worldBound, results);
}
/**
* Sets the {@link VertexBuffer} on the mesh.
* This will update the vertex/triangle counts if needed.
- *
+ *
* @param vb The buffer to set
* @throws IllegalArgumentException If the buffer type is already set
*/
@@ -979,12 +1010,12 @@ public class Mesh implements Savable, Cloneable {
buffersList.add(vb);
updateCounts();
}
-
+
/**
* Unsets the {@link VertexBuffer} set on this mesh
- * with the given type. Does nothing if the vertex buffer type is not set
+ * with the given type. Does nothing if the vertex buffer type is not set
* initially.
- *
+ *
* @param type The buffer type to remove
*/
public void clearBuffer(VertexBuffer.Type type){
@@ -994,17 +1025,17 @@ public class Mesh implements Savable, Cloneable {
updateCounts();
}
}
-
+
/**
* Creates a {@link VertexBuffer} for the mesh or modifies
* the existing one per the parameters given.
- *
+ *
* @param type The type of the buffer
* @param components Number of components
* @param format Data format
* @param buf The buffer data
- *
- * @throws UnsupportedOperationException If the buffer already set is
+ *
+ * @throws UnsupportedOperationException If the buffer already set is
* incompatible with the parameters given.
*/
public void setBuffer(Type type, int components, Format format, Buffer buf){
@@ -1022,16 +1053,16 @@ public class Mesh implements Savable, Cloneable {
updateCounts();
}
}
-
+
/**
- * Set a floating point {@link VertexBuffer} on the mesh.
- *
- * @param type The type of {@link VertexBuffer},
+ * Set a floating point {@link VertexBuffer} on the mesh.
+ *
+ * @param type The type of {@link VertexBuffer},
* e.g. {@link Type#Position}, {@link Type#Normal}, etc.
- *
+ *
* @param components Number of components on the vertex buffer, should
* be between 1 and 4.
- *
+ *
* @param buf The floating point data to contain
*/
public void setBuffer(Type type, int components, FloatBuffer buf) {
@@ -1069,9 +1100,9 @@ public class Mesh implements Savable, Cloneable {
/**
* Get the {@link VertexBuffer} stored on this mesh with the given
* type.
- *
+ *
* @param type The type of VertexBuffer
- * @return the VertexBuffer data, or null if not set
+ * @return the VertexBuffer data, or null if not set
*/
public VertexBuffer getBuffer(Type type){
return buffers.get(type.ordinal());
@@ -1080,7 +1111,7 @@ public class Mesh implements Savable, Cloneable {
/**
* Get the {@link VertexBuffer} data stored on this mesh in float
* format.
- *
+ *
* @param type The type of VertexBuffer
* @return the VertexBuffer data, or null if not set
*/
@@ -1091,11 +1122,11 @@ public class Mesh implements Savable, Cloneable {
return (FloatBuffer) vb.getData();
}
-
+
/**
* Get the {@link VertexBuffer} data stored on this mesh in short
* format.
- *
+ *
* @param type The type of VertexBuffer
* @return the VertexBuffer data, or null if not set
*/
@@ -1110,18 +1141,18 @@ public class Mesh implements Savable, Cloneable {
/**
* Acquires an index buffer that will read the vertices on the mesh
* as a list.
- *
+ *
* @return A virtual or wrapped index buffer to read the data as a list
*/
public IndexBuffer getIndicesAsList(){
if (mode == Mode.Hybrid)
throw new UnsupportedOperationException("Hybrid mode not supported");
-
+
IndexBuffer ib = getIndexBuffer();
if (ib != null){
if (mode.isListMode()){
// already in list mode
- return ib;
+ return ib;
}else{
// not in list mode but it does have an index buffer
// wrap it so the data is converted to list format
@@ -1133,20 +1164,20 @@ public class Mesh implements Savable, Cloneable {
return new VirtualIndexBuffer(vertCount, mode);
}
}
-
+
/**
- * Get the index buffer for this mesh.
+ * Get the index buffer for this mesh.
* Will return null
if no index buffer is set.
- *
+ *
* @return The index buffer of this mesh.
- *
+ *
* @see Type#Index
*/
public IndexBuffer getIndexBuffer() {
VertexBuffer vb = getBuffer(Type.Index);
if (vb == null)
return null;
-
+
return IndexBuffer.wrapIndexBuffer(vb.getData());
}
@@ -1156,7 +1187,7 @@ public class Mesh implements Savable, Cloneable {
* to index into the attributes of the other mesh.
* Note that this will also change this mesh's index buffer so that
* the references to the vertex data match the new indices.
- *
+ *
* @param other The mesh to extract the vertex data from
*/
public void extractVertexData(Mesh other) {
@@ -1176,7 +1207,7 @@ public class Mesh implements Savable, Cloneable {
int oldIndex = indexBuf.get(i);
if (!oldIndicesToNewIndices.containsKey(oldIndex)) {
- // this vertex has not been added, so allocate a
+ // this vertex has not been added, so allocate a
// new index for it and add it to the map
oldIndicesToNewIndices.put(oldIndex, newIndex);
newIndicesToOldIndices.add(oldIndex);
@@ -1193,8 +1224,8 @@ public class Mesh implements Savable, Cloneable {
throw new AssertionError();
}
- // Create the new index buffer.
- // Do not overwrite the old one because we might be able to
+ // Create the new index buffer.
+ // Do not overwrite the old one because we might be able to
// convert from int index buffer to short index buffer
IndexBuffer newIndexBuf;
if (newNumVerts >= 65536) {
@@ -1210,10 +1241,10 @@ public class Mesh implements Savable, Cloneable {
newIndexBuf.put(i, newIndex);
}
-
+
VertexBuffer newIdxBuf = new VertexBuffer(Type.Index);
- newIdxBuf.setupData(oldIdxBuf.getUsage(),
- oldIdxBuf.getNumComponents(),
+ newIdxBuf.setupData(oldIdxBuf.getUsage(),
+ oldIdxBuf.getNumComponents(),
newIndexBuf instanceof IndexIntBuffer ? Format.UnsignedInt : Format.UnsignedShort,
newIndexBuf.getBuffer());
clearBuffer(Type.Index);
@@ -1229,7 +1260,7 @@ public class Mesh implements Savable, Cloneable {
VertexBuffer newVb = new VertexBuffer(oldVb.getBufferType());
newVb.setNormalized(oldVb.isNormalized());
- //check for data before copying, some buffers are just empty shells
+ //check for data before copying, some buffers are just empty shells
//for caching purpose (HW skinning buffers), and will be filled when
//needed
if(oldVb.getData()!=null){
@@ -1247,32 +1278,32 @@ public class Mesh implements Savable, Cloneable {
oldVb.copyElement(oldIndex, newVb, i);
}
}
-
+
// Set the buffer on the mesh
clearBuffer(newVb.getBufferType());
setBuffer(newVb);
}
-
+
// Copy max weights per vertex as well
setMaxNumWeights(other.getMaxNumWeights());
-
+
// The data has been copied over, update informations
updateCounts();
updateBound();
}
-
+
/**
* Scales the texture coordinate buffer on this mesh by the given
- * scale factor.
+ * scale factor.
* - * Note that values above 1 will cause the - * texture to tile, while values below 1 will cause the texture + * Note that values above 1 will cause the + * texture to tile, while values below 1 will cause the texture * to stretch. *
- * + * * @param scaleFactor The scale factor to scale by. Every texture * coordinate is multiplied by this vector to get the result. - * + * * @throws IllegalStateException If there's no texture coordinate * buffer on the mesh * @throws UnsupportedOperationException If the texture coordinate @@ -1304,7 +1335,7 @@ public class Mesh implements Savable, Cloneable { } /** - * Updates the bounding volume of this mesh. + * Updates the bounding volume of this mesh. * The method does nothing if the mesh has no {@link Type#Position} buffer. * It is expected that the position buffer is a float buffer with 3 components. */ @@ -1318,7 +1349,7 @@ public class Mesh implements Savable, Cloneable { /** * Returns the {@link BoundingVolume} of this Mesh. * By default the bounding volume is a {@link BoundingBox}. - * + * * @return the bounding volume of this mesh */ public BoundingVolume getBound() { @@ -1328,7 +1359,7 @@ public class Mesh implements Savable, Cloneable { /** * Sets the {@link BoundingVolume} for this Mesh. * The bounding volume is recomputed by calling {@link #updateBound() }. - * + * * @param modelBound The model bound to set */ public void setBound(BoundingVolume modelBound) { @@ -1339,38 +1370,38 @@ public class Mesh implements Savable, Cloneable { * Returns a map of all {@link VertexBuffer vertex buffers} on this Mesh. * The integer key for the map is the {@link Enum#ordinal() ordinal} * of the vertex buffer's {@link Type}. - * Note that the returned map is a reference to the map used internally, + * Note that the returned map is a reference to the map used internally, * modifying it will cause undefined results. - * + * * @return map of vertex buffers on this mesh. */ public IntMapNode
with a default empty
* list for containing children.
- *
+ *
* @param name the name of the scene element. This is required for
* identification and comparison purposes.
*/
public Node(String name) {
super(name);
-
+
// For backwards compatibility, only clear the "requires
// update" flag if we are not a subclass of Node.
// This prevents subclass from silently failing to receive
// updates when they upgrade.
- setRequiresUpdates(Node.class != getClass());
+ setRequiresUpdates(Node.class != getClass());
}
/**
- *
+ *
* getQuantity
returns the number of children this node
* maintains.
- *
+ *
* @return the number of children this node maintains.
*/
public int getQuantity() {
- return children.size();
+ return children.size();
}
@Override
@@ -143,7 +144,7 @@ public class Node extends Spatial {
@Override
protected void updateWorldBound(){
super.updateWorldBound();
-
+
// for a node, the world bound is a combination of all it's children
// bounds
BoundingVolume resultBound = null;
@@ -167,7 +168,7 @@ public class Node extends Spatial {
protected void setParent(Node parent) {
if( this.parent == null && parent != null ) {
// We were a root before and now we aren't... make sure if
- // we had an updateList then we clear it completely to
+ // we had an updateList then we clear it completely to
// avoid holding the dead array.
updateList = null;
updateListValid = false;
@@ -204,15 +205,15 @@ public class Node extends Spatial {
return updateList;
}
if( updateList == null ) {
- updateList = new SafeArrayListgetTriangleCount
returns the number of triangles contained
* in all sub-branches of this node that contain geometry.
- *
+ *
* @return the triangle count of this branch.
*/
@Override
@@ -286,11 +287,11 @@ public class Node extends Spatial {
return count;
}
-
+
/**
* getVertexCount
returns the number of vertices contained
* in all sub-branches of this node that contain geometry.
- *
+ *
* @return the vertex count of this branch.
*/
@Override
@@ -311,7 +312,7 @@ public class Node extends Spatial {
* returned.
* attachChildAt
attaches a child to this node at an index. This node
* becomes the child's parent. The current number of children maintained is
* returned.
* detachChild
removes a given child from the node's list.
* This child will no longer be maintained.
- *
+ *
* @param child
* the child to remove.
* @return the index the child was at. -1 if the child was not in the list.
@@ -379,16 +380,16 @@ public class Node extends Spatial {
detachChildAt(index);
}
return index;
- }
-
- return -1;
+ }
+
+ return -1;
}
/**
* detachChild
removes a given child from the node's list.
* This child will no longe be maintained. Only the first child with a
* matching name is removed.
- *
+ *
* @param childName
* the child to remove.
* @return the index the child was at. -1 if the child was not in the list.
@@ -408,10 +409,10 @@ public class Node extends Spatial {
}
/**
- *
+ *
* detachChildAt
removes a child at a given index. That child
* is returned for saving purposes.
- *
+ *
* @param index
* the index of the child to be removed.
* @return the child at the supplied index.
@@ -432,14 +433,14 @@ public class Node extends Spatial {
child.setTransformRefresh();
// lights are also inherited from parent
child.setLightListRefresh();
-
+
invalidateUpdateList();
}
return child;
}
/**
- *
+ *
* detachAllChildren
removes all children attached to this
* node.
*/
@@ -458,7 +459,7 @@ public class Node extends Spatial {
* in this node's list of children.
* @param sp
* The spatial to look up
- * @return
+ * @return
* The index of the spatial in the node's children, or -1
* if the spatial is not attached to this node
*/
@@ -468,7 +469,7 @@ public class Node extends Spatial {
/**
* More efficient than e.g detaching and attaching as no updates are needed.
- *
+ *
* @param index1 The index of the first child to swap
* @param index2 The index of the second child to swap
*/
@@ -481,9 +482,9 @@ public class Node extends Spatial {
}
/**
- *
+ *
* getChild
returns a child at a given index.
- *
+ *
* @param i
* the index to retrieve the child from.
* @return the child at a specified index.
@@ -497,13 +498,13 @@ public class Node extends Spatial {
* given name (case sensitive.) This method does a depth first recursive
* search of all descendants of this node, it will return the first spatial
* found with a matching name.
- *
+ *
* @param name
* the name of the child to retrieve. If null, we'll return null.
* @return the child if found, or null.
*/
public Spatial getChild(String name) {
- if (name == null)
+ if (name == null)
return null;
for (Spatial child : children.getArray()) {
@@ -518,11 +519,11 @@ public class Node extends Spatial {
}
return null;
}
-
+
/**
* determines if the provided Spatial is contained in the children list of
* this node.
- *
+ *
* @param spat
* the child object to look for.
* @return true if the object is contained, false otherwise.
@@ -566,39 +567,39 @@ public class Node extends Spatial {
public int collideWith(Collidable other, CollisionResults results){
int total = 0;
-
+
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
- // number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
+ // number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
// The idea is when there are few children, it can be too expensive to test boundingVolume first.
/*
I'm removing this change until some issues can be addressed and I really
think it needs to be implemented a better way anyway.
-
+
First, it causes issues for anyone doing collideWith() with BoundingVolumes
and expecting it to trickle down to the children. For example, children
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
a collision check at the parent level then has to do a BoundingSphere to BoundingBox
collision which isn't resolved. (Having to come up with a collision point in that
case is tricky and the first sign that this is the wrong approach.)
-
+
Second, the rippling changes this caused to 'optimize' collideWith() for this
special use-case are another sign that this approach was a bit dodgy. The whole
idea of calculating a full collision just to see if the two shapes collide at all
is very wasteful.
-
+
A proper implementation should support a simpler boolean check that doesn't do
all of that calculation. For example, if 'other' is also a BoundingVolume (ie: 99.9%
of all non-Ray cases) then a direct BV to BV intersects() test can be done. So much
faster. And if 'other' _is_ a Ray then the BV.intersects(Ray) call can be done.
-
+
I don't have time to do it right now but I'll at least un-break a bunch of peoples'
code until it can be 'optimized' properly. Hopefully it's not too late to back out
- the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
-
+ the other dodgy ripples this caused. -pspeed (hindsight-expert ;))
+
Note: the code itself is relatively simple to implement but I don't have time to
a) test it, and b) see if '> 4' is still a decent check for it. Could be it's fast
enough to do all the time for > 1.
-
+
if (children.size() > 4)
{
BoundingVolume bv = this.getWorldBound();
@@ -642,7 +643,7 @@ public class Node extends Spatial {
* @return Non-null, but possibly 0-element, list of matching Spatials (also Instances extending Spatials).
*
* @see java.util.regex.Pattern
- * @see Spatial#matches(java.lang.Class, java.lang.String)
+ * @see Spatial#matches(java.lang.Class, java.lang.String)
*/
@SuppressWarnings("unchecked")
public Spatial
through the
- * {@link #addLight(com.jme3.light.Light) } and
+ * {@link #addLight(com.jme3.light.Light) } and
* {@link #removeLight(com.jme3.light.Light) } methods.
- *
+ *
* @return The local light list
*/
public LightList getLocalLightList() {
@@ -414,7 +416,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* Returns the world {@link LightList}, containing the lights
* combined from all this Spatial's
parents up to and including
* this Spatial
's lights.
- *
+ *
* @return The combined world light list
*/
public LightList getWorldLightList() {
@@ -502,14 +504,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* lookAt
is a convenience method for auto-setting the local
* rotation based on a position in world space and an up vector. It computes the rotation
* to transform the z-axis to point onto 'position' and the y-axis to 'up'.
- * Unlike {@link Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) }
+ * Unlike {@link Quaternion#lookAt(com.jme3.math.Vector3f, com.jme3.math.Vector3f) }
* this method takes a world position to look at and not a relative direction.
*
* Note : 28/01/2013 this method has been fixed as it was not taking into account the parent rotation.
* This was resulting in improper rotation when the spatial had rotated parent nodes.
- * This method is intended to work in world space, so no matter what parent graph the
+ * This method is intended to work in world space, so no matter what parent graph the
* spatial has, it will look at the given position in world space.
- *
+ *
* @param position
* where to look at in terms of world coordinates
* @param upVector
@@ -522,10 +524,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
TempVars vars = TempVars.get();
Vector3f compVecA = vars.vect4;
-
+
compVecA.set(position).subtractLocal(worldTranslation);
- getLocalRotation().lookAt(compVecA, upVector);
-
+ getLocalRotation().lookAt(compVecA, upVector);
+
if ( getParent() != null ) {
Quaternion rot=vars.quat1;
rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
@@ -579,7 +581,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
}
/**
- * Computes the world transform of this Spatial in the most
+ * Computes the world transform of this Spatial in the most
* efficient manner possible.
*/
void checkDoTransformUpdate() {
@@ -670,7 +672,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* @param vp The ViewPort to which the Spatial is being rendered to.
*
* @see Spatial#addControl(com.jme3.scene.control.Control)
- * @see Spatial#getControl(java.lang.Class)
+ * @see Spatial#getControl(java.lang.Class)
*/
public void runControlRender(RenderManager rm, ViewPort vp) {
if (controls.isEmpty()) {
@@ -686,26 +688,26 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* Add a control to the list of controls.
* @param control The control to add.
*
- * @see Spatial#removeControl(java.lang.Class)
+ * @see Spatial#removeControl(java.lang.Class)
*/
public void addControl(Control control) {
boolean before = requiresUpdates();
controls.add(control);
control.setSpatial(this);
boolean after = requiresUpdates();
-
+
// If the requirement to be updated has changed
// then we need to let the parent node know so it
// can rebuild its update list.
if( parent != null && before != after ) {
- parent.invalidateUpdateList();
+ parent.invalidateUpdateList();
}
}
/**
* Removes the first control that is an instance of the given class.
*
- * @see Spatial#addControl(com.jme3.scene.control.Control)
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
*/
public void removeControl(Class extends Control> controlType) {
boolean before = requiresUpdates();
@@ -717,23 +719,23 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
}
}
boolean after = requiresUpdates();
-
+
// If the requirement to be updated has changed
// then we need to let the parent node know so it
// can rebuild its update list.
if( parent != null && before != after ) {
- parent.invalidateUpdateList();
+ parent.invalidateUpdateList();
}
}
/**
* Removes the given control from this spatial's controls.
- *
+ *
* @param control The control to remove
* @return True if the control was successfully removed. False if the
* control is not assigned to this spatial.
*
- * @see Spatial#addControl(com.jme3.scene.control.Control)
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
*/
public boolean removeControl(Control control) {
boolean before = requiresUpdates();
@@ -743,14 +745,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
}
boolean after = requiresUpdates();
-
+
// If the requirement to be updated has changed
// then we need to let the parent node know so it
// can rebuild its update list.
if( parent != null && before != after ) {
- parent.invalidateUpdateList();
+ parent.invalidateUpdateList();
}
-
+
return result;
}
@@ -761,7 +763,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* @param controlType The superclass of the control to look for.
* @return The first instance in the list of the controlType class, or null.
*
- * @see Spatial#addControl(com.jme3.scene.control.Control)
+ * @see Spatial#addControl(com.jme3.scene.control.Control)
*/
public removeLight
removes the given light from the Spatial.
- *
+ *
* @param light The light to remove.
- * @see Spatial#addLight(com.jme3.light.Light)
+ * @see Spatial#addLight(com.jme3.light.Light)
*/
public void removeLight(Light light) {
localLights.remove(light);
@@ -1264,7 +1266,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* All controls will be cloned using the Control.cloneForSpatial method
* on the clone.
*
- * @see Mesh#cloneForAnim()
+ * @see Mesh#cloneForAnim()
*/
public Spatial clone(boolean cloneMaterial) {
try {
@@ -1328,7 +1330,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* All controls will be cloned using the Control.cloneForSpatial method
* on the clone.
*
- * @see Mesh#cloneForAnim()
+ * @see Mesh#cloneForAnim()
*/
@Override
public Spatial clone() {
@@ -1344,13 +1346,59 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/
public abstract Spatial deepClone();
+ /**
+ * Called internally by com.jme3.util.clone.Cloner. Do not call directly.
+ */
+ @Override
+ public Spatial jmeClone() {
+ try {
+ Spatial clone = (Spatial)super.clone();
+ return 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 ) {
+
+ // Clone all of the fields that need fix-ups and/or potential
+ // sharing.
+ this.parent = cloner.clone(parent);
+ this.worldBound = cloner.clone(worldBound);
+ this.worldLights = cloner.clone(worldLights);
+ this.localLights = cloner.clone(localLights);
+ this.worldTransform = cloner.clone(worldTransform);
+ this.localTransform = cloner.clone(localTransform);
+ this.controls = cloner.clone(controls);
+
+ // Cloner doesn't handle maps on its own just yet.
+ // Note: this is more advanced cloning than the old clone() method
+ // did because it just shallow cloned the map. In this case, we want
+ // to avoid all of the nasty cloneForSpatial() fixup style code that
+ // used to inject stuff into the clone's user data. By using cloner
+ // to clone the user data we get this automatically.
+ userData = (HashMapsetQueueBucket
determines at what phase of the
* rendering process this Spatial will rendered. See the
- * {@link Bucket} enum for an explanation of the various
+ * {@link Bucket} enum for an explanation of the various
* render queue buckets.
- *
+ *
* @param queueBucket
* The bucket to use for this Spatial.
*/
@@ -1595,7 +1643,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*
* @return store if not null, otherwise, a new matrix containing the result.
*
- * @see Spatial#getWorldTransform()
+ * @see Spatial#getWorldTransform()
*/
public Matrix4f getLocalToWorldMatrix(Matrix4f store) {
if (store == null) {
diff --git a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java
index 7f0bb601b..b57345664 100644
--- a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java
+++ b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java
@@ -47,19 +47,20 @@ import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.util.BufferUtils;
import com.jme3.util.TempVars;
+import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
public class InstancedGeometry extends Geometry {
-
+
private static final int INSTANCE_SIZE = 16;
-
+
private VertexBuffer[] globalInstanceData;
private VertexBuffer transformInstanceData;
private Geometry[] geometries = new Geometry[1];
-
+
private int firstUnusedIndex = 0;
/**
@@ -71,12 +72,12 @@ public class InstancedGeometry extends Geometry {
setBatchHint(BatchHint.Never);
setMaxNumInstances(1);
}
-
+
/**
* Creates instanced geometry with the specified mode and name.
- *
- * @param name The name of the spatial.
- *
+ *
+ * @param name The name of the spatial.
+ *
* @see Spatial#Spatial(java.lang.String)
*/
public InstancedGeometry(String name) {
@@ -85,57 +86,57 @@ public class InstancedGeometry extends Geometry {
setBatchHint(BatchHint.Never);
setMaxNumInstances(1);
}
-
+
/**
- * Global user specified per-instance data.
- *
+ * Global user specified per-instance data.
+ *
* By default set to null
, specify an array of VertexBuffers
* via {@link #setGlobalUserInstanceData(com.jme3.scene.VertexBuffer[]) }.
- *
- * @return global user specified per-instance data.
- * @see #setGlobalUserInstanceData(com.jme3.scene.VertexBuffer[])
+ *
+ * @return global user specified per-instance data.
+ * @see #setGlobalUserInstanceData(com.jme3.scene.VertexBuffer[])
*/
public VertexBuffer[] getGlobalUserInstanceData() {
return globalInstanceData;
}
-
+
/**
* Specify global user per-instance data.
- *
+ *
* By default set to null
, specify an array of VertexBuffers
* that contain per-instance vertex attributes.
- *
+ *
* @param globalInstanceData global user per-instance data.
- *
- * @throws IllegalArgumentException If one of the VertexBuffers is not
+ *
+ * @throws IllegalArgumentException If one of the VertexBuffers is not
* {@link VertexBuffer#setInstanced(boolean) instanced}.
*/
public void setGlobalUserInstanceData(VertexBuffer[] globalInstanceData) {
this.globalInstanceData = globalInstanceData;
}
-
+
/**
* Specify camera specific user per-instance data.
- *
+ *
* @param transformInstanceData The transforms for each instance.
*/
public void setTransformUserInstanceData(VertexBuffer transformInstanceData) {
this.transformInstanceData = transformInstanceData;
}
-
+
/**
* Return user per-instance transform data.
- *
+ *
* @return The per-instance transform data.
*
- * @see #setTransformUserInstanceData(com.jme3.scene.VertexBuffer)
+ * @see #setTransformUserInstanceData(com.jme3.scene.VertexBuffer)
*/
public VertexBuffer getTransformUserInstanceData() {
return transformInstanceData;
}
-
- private void updateInstance(Matrix4f worldMatrix, float[] store,
- int offset, Matrix3f tempMat3,
+
+ private void updateInstance(Matrix4f worldMatrix, float[] store,
+ int offset, Matrix3f tempMat3,
Quaternion tempQuat) {
worldMatrix.toRotationMatrix(tempMat3);
tempMat3.invertLocal();
@@ -164,17 +165,17 @@ public class InstancedGeometry extends Geometry {
store[offset + 14] = worldMatrix.m23;
store[offset + 15] = tempQuat.getW();
}
-
+
/**
* Set the maximum amount of instances that can be rendered by this
* instanced geometry when mode is set to auto.
- *
+ *
* This re-allocates internal structures and therefore should be called
- * only when necessary.
- *
+ * only when necessary.
+ *
* @param maxNumInstances The maximum number of instances that can be
* rendered.
- *
+ *
* @throws IllegalStateException If mode is set to manual.
* @throws IllegalArgumentException If maxNumInstances is zero or negative
*/
@@ -182,14 +183,14 @@ public class InstancedGeometry extends Geometry {
if (maxNumInstances < 1) {
throw new IllegalArgumentException("maxNumInstances must be 1 or higher");
}
-
+
Geometry[] originalGeometries = geometries;
this.geometries = new Geometry[maxNumInstances];
-
+
if (originalGeometries != null) {
System.arraycopy(originalGeometries, 0, geometries, 0, originalGeometries.length);
}
-
+
// Resize instance data.
if (transformInstanceData != null) {
BufferUtils.destroyDirectBuffer(transformInstanceData.getData());
@@ -203,7 +204,7 @@ public class InstancedGeometry extends Geometry {
BufferUtils.createFloatBuffer(geometries.length * INSTANCE_SIZE));
}
}
-
+
public int getMaxNumInstances() {
return geometries.length;
}
@@ -211,12 +212,12 @@ public class InstancedGeometry extends Geometry {
public int getActualNumInstances() {
return firstUnusedIndex;
}
-
+
private void swap(int idx1, int idx2) {
Geometry g = geometries[idx1];
geometries[idx1] = geometries[idx2];
geometries[idx2] = g;
-
+
if (geometries[idx1] != null) {
InstancedNode.setGeometryStartIndex2(geometries[idx1], idx1);
}
@@ -224,7 +225,7 @@ public class InstancedGeometry extends Geometry {
InstancedNode.setGeometryStartIndex2(geometries[idx2], idx2);
}
}
-
+
private void sanitize(boolean insideEntriesNonNull) {
if (firstUnusedIndex >= geometries.length) {
throw new AssertionError();
@@ -234,7 +235,7 @@ public class InstancedGeometry extends Geometry {
if (geometries[i] == null) {
if (insideEntriesNonNull) {
throw new AssertionError();
- }
+ }
} else if (InstancedNode.getGeometryStartIndex2(geometries[i]) != i) {
throw new AssertionError();
}
@@ -245,55 +246,55 @@ public class InstancedGeometry extends Geometry {
}
}
}
-
+
public void updateInstances() {
FloatBuffer fb = (FloatBuffer) transformInstanceData.getData();
fb.limit(fb.capacity());
fb.position(0);
-
+
TempVars vars = TempVars.get();
{
float[] temp = vars.matrixWrite;
-
+
for (int i = 0; i < firstUnusedIndex; i++) {
Geometry geom = geometries[i];
if (geom == null) {
geom = geometries[firstUnusedIndex - 1];
-
+
if (geom == null) {
throw new AssertionError();
}
-
+
swap(i, firstUnusedIndex - 1);
-
+
while (geometries[firstUnusedIndex -1] == null) {
firstUnusedIndex--;
}
}
-
+
Matrix4f worldMatrix = geom.getWorldMatrix();
updateInstance(worldMatrix, temp, 0, vars.tempMat3, vars.quat1);
fb.put(temp);
}
}
vars.release();
-
+
fb.flip();
-
+
if (fb.limit() / INSTANCE_SIZE != firstUnusedIndex) {
throw new AssertionError();
}
transformInstanceData.updateData(fb);
}
-
+
public void deleteInstance(Geometry geom) {
int idx = InstancedNode.getGeometryStartIndex2(geom);
InstancedNode.setGeometryStartIndex2(geom, -1);
-
+
geometries[idx] = null;
-
+
if (idx == firstUnusedIndex - 1) {
// Deleting the last element.
// Move index back.
@@ -309,12 +310,12 @@ public class InstancedGeometry extends Geometry {
// Deleting element in the middle
}
}
-
+
public void addInstance(Geometry geometry) {
if (geometry == null) {
throw new IllegalArgumentException("geometry cannot be null");
}
-
+
// Take an index from the end.
if (firstUnusedIndex + 1 >= geometries.length) {
// No more room.
@@ -323,15 +324,15 @@ public class InstancedGeometry extends Geometry {
int freeIndex = firstUnusedIndex;
firstUnusedIndex++;
-
+
geometries[freeIndex] = geometry;
InstancedNode.setGeometryStartIndex2(geometry, freeIndex);
}
-
+
public Geometry[] getGeometries() {
return geometries;
}
-
+
public VertexBuffer[] getAllInstanceData() {
ArrayList