Started implementing the JmeCloneable stuff for Spatial

and Mesh.  Still need to catch some of the outer subclasses of
Node and Geometry.  Nothing is hooked up or tested yet.
cleanup_build_scripts
Paul Speed 9 years ago
parent 911b4be868
commit 2bdb3de2f5
  1. 28
      jme3-core/src/main/java/com/jme3/app/StatsView.java
  2. 56
      jme3-core/src/main/java/com/jme3/light/LightList.java
  3. 17
      jme3-core/src/main/java/com/jme3/scene/AssetLinkNode.java
  4. 83
      jme3-core/src/main/java/com/jme3/scene/BatchNode.java
  5. 14
      jme3-core/src/main/java/com/jme3/scene/CameraNode.java
  6. 115
      jme3-core/src/main/java/com/jme3/scene/Geometry.java
  7. 18
      jme3-core/src/main/java/com/jme3/scene/LightNode.java
  8. 405
      jme3-core/src/main/java/com/jme3/scene/Mesh.java
  9. 144
      jme3-core/src/main/java/com/jme3/scene/Node.java
  10. 180
      jme3-core/src/main/java/com/jme3/scene/Spatial.java
  11. 125
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java
  12. 123
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java
  13. 59
      jme3-core/src/main/java/com/jme3/util/IntMap.java
  14. 3
      jme3-core/src/main/resources/com/jme3/system/version.properties

@ -69,7 +69,7 @@ public class StatsView extends Node implements Control, JmeCloneable {
private int[] statData;
private boolean enabled = true;
private final StringBuilder stringBuilder = new StringBuilder();
public StatsView(String name, AssetManager manager, Statistics stats){
@ -95,22 +95,22 @@ public class StatsView extends Node implements Control, JmeCloneable {
public float getHeight() {
return statText.getLineHeight() * statLabels.length;
}
public void update(float tpf) {
if (!isEnabled())
if (!isEnabled())
return;
statistics.getData(statData);
stringBuilder.setLength(0);
// Need to walk through it backwards, as the first label
// Need to walk through it backwards, as the first label
// should appear at the bottom, not the top.
for (int i = statLabels.length - 1; i >= 0; i--) {
stringBuilder.append(statLabels[i]).append(" = ").append(statData[i]).append('\n');
}
statText.setText(stringBuilder);
// Moved to ResetStatsState to make sure it is
// done even if there is no StatsView or the StatsView
// is disable.
@ -122,16 +122,16 @@ public class StatsView extends Node implements Control, JmeCloneable {
return (Control) spatial;
}
@Override
public Object jmeClone() {
@Override
public StatsView jmeClone() {
throw new UnsupportedOperationException("Not yet implemented.");
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
@Override
public void cloneFields( Cloner cloner, Object original ) {
throw new UnsupportedOperationException("Not yet implemented.");
}
public void setSpatial(Spatial spatial) {
}

@ -33,6 +33,8 @@ package com.jme3.light;
import com.jme3.export.*;
import com.jme3.scene.Spatial;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import com.jme3.util.SortUtil;
import java.io.IOException;
import java.util.*;
@ -40,10 +42,10 @@ import java.util.*;
/**
* <code>LightList</code> is used internally by {@link Spatial}s to manage
* lights that are attached to them.
*
*
* @author Kirill Vainer
*/
public final class LightList implements Iterable<Light>, Savable, Cloneable {
public final class LightList implements Iterable<Light>, Savable, Cloneable, JmeCloneable {
private Light[] list, tlist;
private float[] distToOwner;
@ -74,7 +76,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
/**
* Creates a <code>LightList</code> for the given {@link Spatial}.
*
*
* @param owner The spatial owner
*/
public LightList(Spatial owner) {
@ -87,7 +89,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
/**
* Set the owner of the LightList. Only used for cloning.
* @param owner
* @param owner
*/
public void setOwner(Spatial owner){
this.owner = owner;
@ -118,7 +120,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
/**
* Remove the light at the given index.
*
*
* @param index
*/
public void remove(int index){
@ -139,7 +141,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
/**
* Removes the given light from the LightList.
*
*
* @param l the light to remove
*/
public void remove(Light l){
@ -187,12 +189,12 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
/**
* Sorts the elements in the list according to their Comparator.
* There are two reasons why lights should be resorted.
* First, if the lights have moved, that means their distance to
* the spatial changed.
* Second, if the spatial itself moved, it means the distance from it to
* There are two reasons why lights should be resorted.
* First, if the lights have moved, that means their distance to
* the spatial changed.
* Second, if the spatial itself moved, it means the distance from it to
* the individual lights might have changed.
*
*
*
* @param transformChanged Whether the spatial's transform has changed
*/
@ -252,7 +254,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
list[p] = parent.list[i];
distToOwner[p] = Float.NEGATIVE_INFINITY;
}
listSize = local.listSize + parent.listSize;
}else{
listSize = local.listSize;
@ -261,7 +263,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
/**
* Returns an iterator that can be used to iterate over this LightList.
*
*
* @return an iterator that can be used to iterate over this LightList.
*/
public Iterator<Light> iterator() {
@ -276,10 +278,10 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
public Light next() {
if (!hasNext())
throw new NoSuchElementException();
return list[index++];
}
public void remove() {
LightList.this.remove(--index);
}
@ -290,7 +292,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
public LightList clone(){
try{
LightList clone = (LightList) super.clone();
clone.owner = null;
clone.list = list.clone();
clone.distToOwner = distToOwner.clone();
@ -302,6 +304,24 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
}
}
@Override
public LightList jmeClone() {
try{
LightList clone = (LightList)super.clone();
clone.tlist = null; // list used for sorting only
return clone;
}catch (CloneNotSupportedException ex){
throw new AssertionError();
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.owner = cloner.clone(owner);
this.list = cloner.clone(list);
this.distToOwner = cloner.clone(distToOwner);
}
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
// oc.write(owner, "owner", null);
@ -319,7 +339,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
List<Light> lights = ic.readSavableArrayList("lights", null);
listSize = lights.size();
// NOTE: make sure the array has a length of at least 1
int arraySize = Math.max(DEFAULT_SIZE, listSize);
list = new Light[arraySize];
@ -328,7 +348,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable {
for (int i = 0; i < listSize; i++){
list[i] = lights.get(i);
}
Arrays.fill(distToOwner, Float.NEGATIVE_INFINITY);
}

@ -39,6 +39,7 @@ import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.binary.BinaryImporter;
import com.jme3.util.clone.Cloner;
import com.jme3.util.SafeArrayList;
import java.io.IOException;
import java.util.*;
@ -50,7 +51,7 @@ import java.util.logging.Logger;
* The AssetLinkNode does not store its children when exported to file.
* Instead, you can add a list of AssetKeys that will be loaded and attached
* when the AssetLinkNode is restored.
*
*
* @author normenhansen
*/
public class AssetLinkNode extends Node {
@ -70,6 +71,18 @@ public class AssetLinkNode extends Node {
assetLoaderKeys.add(key);
}
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
// This is a change in behavior because the old version did not clone
// this list... changes to one clone would be reflected in all.
// I think that's probably undesirable. -pspeed
this.assetLoaderKeys = cloner.clone(assetLoaderKeys);
this.assetChildren = new HashMap<ModelKey, Spatial>();
}
/**
* Add a "linked" child. These are loaded from the assetManager when the
* AssetLinkNode is loaded from a binary file.
@ -166,7 +179,7 @@ public class AssetLinkNode extends Node {
children.add(child);
assetChildren.put(modelKey, child);
} else {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot locate {0} for asset link node {1}",
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot locate {0} for asset link node {1}",
new Object[]{ modelKey, key });
}
}

@ -48,6 +48,8 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
/**
* BatchNode holds geometries that are a batched version of all the geometries that are in its sub scenegraph.
@ -60,7 +62,7 @@ import com.jme3.util.TempVars;
* Sub geoms can be removed but it may be slower than the normal spatial removing
* Sub geoms can be added after the batch() method has been called but won't be batched and will just be rendered as normal geometries.
* To integrate them in the batch you have to call the batch() method again on the batchNode.
*
*
* TODO normal or tangents or both looks a bit weird
* TODO more automagic (batch when needed in the updateLogicalState)
* @author Nehon
@ -77,7 +79,7 @@ public class BatchNode extends GeometryGroupNode {
*/
protected Map<Geometry, Batch> batchesByGeom = new HashMap<Geometry, Batch>();
/**
* used to store transformed vectors before proceeding to a bulk put into the FloatBuffer
* used to store transformed vectors before proceeding to a bulk put into the FloatBuffer
*/
private float[] tmpFloat;
private float[] tmpFloatN;
@ -96,7 +98,7 @@ public class BatchNode extends GeometryGroupNode {
public BatchNode(String name) {
super(name);
}
@Override
public void onTransformChange(Geometry geom) {
updateSubBatch(geom);
@ -123,7 +125,7 @@ public class BatchNode extends GeometryGroupNode {
protected Matrix4f getTransformMatrix(Geometry g){
return g.cachedWorldMat;
}
protected void updateSubBatch(Geometry bg) {
Batch batch = batchesByGeom.get(bg);
if (batch != null) {
@ -134,13 +136,13 @@ public class BatchNode extends GeometryGroupNode {
FloatBuffer posBuf = (FloatBuffer) pvb.getData();
VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
FloatBuffer normBuf = (FloatBuffer) nvb.getData();
VertexBuffer opvb = origMesh.getBuffer(VertexBuffer.Type.Position);
FloatBuffer oposBuf = (FloatBuffer) opvb.getData();
VertexBuffer onvb = origMesh.getBuffer(VertexBuffer.Type.Normal);
FloatBuffer onormBuf = (FloatBuffer) onvb.getData();
Matrix4f transformMat = getTransformMatrix(bg);
if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
@ -184,12 +186,12 @@ public class BatchNode extends GeometryGroupNode {
}
batches.clear();
batchesByGeom.clear();
}
}
//only reset maxVertCount if there is something new to batch
if (matMap.size() > 0) {
maxVertCount = 0;
}
for (Map.Entry<Material, List<Geometry>> entry : matMap.entrySet()) {
Mesh m = new Mesh();
Material material = entry.getKey();
@ -255,7 +257,7 @@ public class BatchNode extends GeometryGroupNode {
/**
* recursively visit the subgraph and unbatch geometries
* @param s
* @param s
*/
private void unbatchSubGraph(Spatial s) {
if (s instanceof Node) {
@ -269,8 +271,8 @@ public class BatchNode extends GeometryGroupNode {
}
}
}
private void gatherGeometries(Map<Material, List<Geometry>> map, Spatial n, boolean rebatch) {
if (n instanceof Geometry) {
@ -283,7 +285,7 @@ public class BatchNode extends GeometryGroupNode {
}
List<Geometry> list = map.get(g.getMaterial());
if (list == null) {
//trying to compare materials with the isEqual method
//trying to compare materials with the isEqual method
for (Map.Entry<Material, List<Geometry>> mat : map.entrySet()) {
if (g.getMaterial().contentEquals(mat.getKey())) {
list = mat.getValue();
@ -331,7 +333,7 @@ public class BatchNode extends GeometryGroupNode {
/**
* Sets the material to the all the batches of this BatchNode
* use setMaterial(Material material,int batchIndex) to set a material to a specific batch
*
*
* @param material the material to use for this geometry
*/
@Override
@ -341,12 +343,12 @@ public class BatchNode extends GeometryGroupNode {
/**
* Returns the material that is used for the first batch of this BatchNode
*
*
* use getMaterial(Material material,int batchIndex) to get a material from a specific batch
*
*
* @return the material that is used for the first batch of this BatchNode
*
* @see #setMaterial(com.jme3.material.Material)
*
* @see #setMaterial(com.jme3.material.Material)
*/
public Material getMaterial() {
if (!batches.isEmpty()) {
@ -359,7 +361,7 @@ public class BatchNode extends GeometryGroupNode {
/**
* Merges all geometries in the collection into
* the output mesh. Does not take into account materials.
*
*
* @param geometries
* @param outMesh
*/
@ -418,7 +420,7 @@ public class BatchNode extends GeometryGroupNode {
formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
normForBuf[vb.getBufferType().ordinal()] = vb.isNormalized();
}
maxWeights = Math.max(maxWeights, geom.getMesh().getMaxNumWeights());
if (mode != null && mode != listMode) {
@ -586,7 +588,7 @@ public class BatchNode extends GeometryGroupNode {
int offset = start * 3;
int tanOffset = start * 4;
bindBufPos.rewind();
bindBufNorm.rewind();
bindBufTangents.rewind();
@ -662,10 +664,10 @@ public class BatchNode extends GeometryGroupNode {
vars.release();
}
protected class Batch {
protected class Batch implements JmeCloneable {
/**
* update the batchesByGeom map for this batch with the given List of geometries
* @param list
* @param list
*/
void updateGeomList(List<Geometry> list) {
for (Geometry geom : list) {
@ -675,10 +677,25 @@ public class BatchNode extends GeometryGroupNode {
}
}
Geometry geometry;
public final Geometry getGeometry() {
return geometry;
}
@Override
public Batch jmeClone() {
try {
return (Batch)super.clone();
} catch (CloneNotSupportedException ex) {
throw new AssertionError();
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.geometry = cloner.clone(geometry);
}
}
protected void setNeedsFullRebatch(boolean needsFullRebatch) {
@ -704,7 +721,25 @@ public class BatchNode extends GeometryGroupNode {
}
return clone;
}
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.batches = cloner.clone(batches);
this.tmpFloat = cloner.clone(tmpFloat);
this.tmpFloatN = cloner.clone(tmpFloatN);
this.tmpFloatT = cloner.clone(tmpFloatT);
HashMap<Geometry, Batch> newBatchesByGeom = new HashMap<Geometry, Batch>();
for( Map.Entry<Geometry, Batch> e : batchesByGeom.entrySet() ) {
newBatchesByGeom.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
}
this.batchesByGeom = newBatchesByGeom;
}
@Override
public int collideWith(Collidable other, CollisionResults results) {
int total = 0;

@ -36,6 +36,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.renderer.Camera;
import com.jme3.scene.control.CameraControl;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.util.clone.Cloner;
import java.io.IOException;
/**
@ -93,7 +94,18 @@ public class CameraNode extends Node {
// this.lookAt(position, upVector);
// camControl.getCamera().lookAt(position, upVector);
// }
/**
* 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 CameraNode was probably
// not really cloneable... or at least its camControl would be pointing
// to the wrong control. -pspeed
this.camControl = cloner.clone(camControl);
}
@Override
public void read(JmeImporter im) throws IOException {
super.read(im);

@ -43,6 +43,7 @@ import com.jme3.material.Material;
import com.jme3.math.Matrix4f;
import com.jme3.renderer.Camera;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.clone.Cloner;
import com.jme3.util.TempVars;
import java.io.IOException;
import java.util.Queue;
@ -54,12 +55,12 @@ import java.util.logging.Logger;
* contains the geometric data for rendering objects. It manages all rendering
* information such as a {@link Material} object to define how the surface
* should be shaded and the {@link Mesh} data to contain the actual geometry.
*
*
* @author Kirill Vainer
*/
public class Geometry extends Spatial {
// Version #1: removed shared meshes.
// Version #1: removed shared meshes.
// models loaded with shared mesh will be automatically fixed.
public static final int SAVABLE_VERSION = 1;
private static final Logger logger = Logger.getLogger(Geometry.class.getName());
@ -71,19 +72,19 @@ public class Geometry extends Spatial {
*/
protected boolean ignoreTransform = false;
protected transient Matrix4f cachedWorldMat = new Matrix4f();
/**
* Specifies which {@link GeometryGroupNode} this <code>Geometry</code>
* is managed by.
*/
protected GeometryGroupNode groupNode;
/**
* The start index of this <code>Geometry's</code> inside
* The start index of this <code>Geometry's</code> 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 <code>Geometry</code> 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
* <code>Geometry</code>.
*
*
* 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 <code>Geometry</code> is managed by a
* Determine whether this <code>Geometry</code> 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);

@ -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;
/**
* <code>LightNode</code> 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 <code>LightNode</code> 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);

File diff suppressed because it is too large Load Diff

@ -40,6 +40,7 @@ import com.jme3.export.Savable;
import com.jme3.material.Material;
import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars;
import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -53,7 +54,7 @@ import java.util.logging.Logger;
* node maintains a collection of children and handles merging said children
* into a single bound to allow for very fast culling of multiple nodes. Node
* allows for any number of children to be attached.
*
*
* @author Mark Powell
* @author Gregg Patton
* @author Joshua Slack
@ -62,26 +63,26 @@ public class Node extends Spatial {
private static final Logger logger = Logger.getLogger(Node.class.getName());
/**
/**
* This node's children.
*/
protected SafeArrayList<Spatial> children = new SafeArrayList<Spatial>(Spatial.class);
/**
* If this node is a root, this list will contain the current
* set of children (and children of children) that require
* set of children (and children of children) that require
* updateLogicalState() to be called as indicated by their
* requiresUpdate() method.
*/
private SafeArrayList<Spatial> updateList = null;
/**
* False if the update list requires rebuilding. This is Node.class
* specific and therefore not included as part of the Spatial update flags.
* A flag is used instead of nulling the updateList to avoid reallocating
* a whole list every time the scene graph changes.
*/
private boolean updateListValid = false;
*/
private boolean updateListValid = false;
/**
* Serialization only. Do not use.
@ -93,29 +94,29 @@ public class Node extends Spatial {
/**
* Constructor instantiates a new <code>Node</code> 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());
}
/**
*
*
* <code>getQuantity</code> 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 SafeArrayList<Spatial>(Spatial.class);
updateList = new SafeArrayList<Spatial>(Spatial.class);
} else {
updateList.clear();
}
// Build the list
addUpdateChildren(updateList);
updateListValid = true;
return updateList;
updateListValid = true;
return updateList;
}
@Override
@ -238,7 +239,7 @@ public class Node extends Spatial {
// This branch has no geometric state that requires updates.
return;
}
if ((refreshFlags & RF_LIGHTLIST) != 0){
updateWorldLightList();
}
@ -250,7 +251,7 @@ public class Node extends Spatial {
}
refreshFlags &= ~RF_CHILD_LIGHTLIST;
if (!children.isEmpty()) {
// the important part- make sure child geometric state is refreshed
// first before updating own world bound. This saves
@ -260,7 +261,7 @@ public class Node extends Spatial {
for (Spatial child : children.getArray()) {
child.updateGeometricState();
}
}
}
if ((refreshFlags & RF_BOUND) != 0){
updateWorldBound();
@ -272,7 +273,7 @@ public class Node extends Spatial {
/**
* <code>getTriangleCount</code> 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;
}
/**
* <code>getVertexCount</code> 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.
* <br>
* If the child already had a parent it is detached from that former parent.
*
*
* @param child
* the child to attach to this node.
* @return the number of children maintained by this node.
@ -320,15 +321,15 @@ public class Node extends Spatial {
public int attachChild(Spatial child) {
return attachChildAt(child, children.size());
}
/**
*
*
* <code>attachChildAt</code> attaches a child to this node at an index. This node
* becomes the child's parent. The current number of children maintained is
* returned.
* <br>
* If the child already had a parent it is detached from that former parent.
*
*
* @param child
* the child to attach to this node.
* @return the number of children maintained by this node.
@ -344,7 +345,7 @@ public class Node extends Spatial {
}
child.setParent(this);
children.add(index, child);
// XXX: Not entirely correct? Forces bound update up the
// tree stemming from the attached child. Also forces
// transform update down the tree-
@ -354,17 +355,17 @@ public class Node extends Spatial {
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
new Object[]{child.getName(), getName()});
}
invalidateUpdateList();
}
return children.size();
}
/**
* <code>detachChild</code> 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;
}
/**
* <code>detachChild</code> 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 {
}
/**
*
*
* <code>detachChildAt</code> 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;
}
/**
*
*
* <code>detachAllChildren</code> 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 {
}
/**
*
*
* <code>getChild</code> 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 <T extends Spatial>List<T> descendantMatches(
@ -662,7 +663,7 @@ public class Node extends Spatial {
/**
* Convenience wrapper.
*
* @see #descendantMatches(java.lang.Class, java.lang.String)
* @see #descendantMatches(java.lang.Class, java.lang.String)
*/
public <T extends Spatial>List<T> descendantMatches(
Class<T> spatialSubclass) {
@ -672,7 +673,7 @@ public class Node extends Spatial {
/**
* Convenience wrapper.
*
* @see #descendantMatches(java.lang.Class, java.lang.String)
* @see #descendantMatches(java.lang.Class, java.lang.String)
*/
public <T extends Spatial>List<T> descendantMatches(String nameRegex) {
return descendantMatches(null, nameRegex);
@ -691,7 +692,7 @@ public class Node extends Spatial {
// Reset the fields of the clone that should be in a 'new' state.
nodeClone.updateList = null;
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
return nodeClone;
}
@ -707,6 +708,19 @@ public class Node extends Spatial {
return nodeClone;
}
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.children = cloner.clone(children);
// Only the outer cloning thing knows whether this should be nulled
// or not... after all, we might be cloning a root node in which case
// cloning this list is fine.
this.updateList = cloner.clone(updateList);
}
@Override
public void write(JmeExporter e) throws IOException {
super.write(e);
@ -718,8 +732,8 @@ public class Node extends Spatial {
// XXX: Load children before loading itself!!
// This prevents empty children list if controls query
// it in Control.setSpatial().
children = new SafeArrayList( Spatial.class,
children = new SafeArrayList( Spatial.class,
e.getCapsule(this).readSavableArrayList("children", null) );
// go through children and set parent to this node
@ -728,7 +742,7 @@ public class Node extends Spatial {
child.parent = this;
}
}
super.read(e);
}
@ -749,7 +763,7 @@ public class Node extends Spatial {
}
}
}
@Override
public void depthFirstTraversal(SceneGraphVisitor visitor) {
for (Spatial child : children.getArray()) {
@ -757,7 +771,7 @@ public class Node extends Spatial {
}
visitor.visit(this);
}
@Override
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
queue.addAll(children);

@ -47,6 +47,8 @@ import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.control.Control;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import com.jme3.util.SafeArrayList;
import com.jme3.util.TempVars;
import java.io.IOException;
@ -63,17 +65,17 @@ import java.util.logging.Logger;
* @author Joshua Slack
* @version $Revision: 4075 $, $Data$
*/
public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset {
public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset, JmeCloneable {
private static final Logger logger = Logger.getLogger(Spatial.class.getName());
/**
* Specifies how frustum culling should be handled by
* Specifies how frustum culling should be handled by
* this spatial.
*/
public enum CullHint {
/**
/**
* Do whatever our parent does. If no parent, default to {@link #Dynamic}.
*/
Inherit,
@ -83,13 +85,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* Camera planes whether or not this Spatial should be culled.
*/
Dynamic,
/**
/**
* Always cull this from the view, throwing away this object
* and any children from rendering commands.
*/
Always,
/**
* Never cull this from view, always draw it.
* Never cull this from view, always draw it.
* Note that we will still get culled if our parent is culled.
*/
Never;
@ -100,15 +102,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/
public enum BatchHint {
/**
/**
* Do whatever our parent does. If no parent, default to {@link #Always}.
*/
Inherit,
/**
/**
* This spatial will always be batched when attached to a BatchNode.
*/
Always,
/**
/**
* This spatial will never be batched when attached to a BatchNode.
*/
Never;
@ -118,12 +120,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
RF_BOUND = 0x02,
RF_LIGHTLIST = 0x04, // changes in light lists
RF_LIGHTLIST = 0x04, // changes in light lists
RF_CHILD_LIGHTLIST = 0x08; // some child need geometry update
protected CullHint cullHint = CullHint.Inherit;
protected BatchHint batchHint = BatchHint.Inherit;
/**
/**
* Spatial's bounding volume relative to the world.
*/
protected BoundingVolume worldBound;
@ -132,7 +134,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/
protected LightList localLights;
protected transient LightList worldLights;
/**
/**
* This spatial's name.
*/
protected String name;
@ -147,11 +149,11 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
protected HashMap<String, Savable> userData = null;
/**
* Used for smart asset caching
*
* @see AssetKey#useSmartCache()
*
* @see AssetKey#useSmartCache()
*/
protected AssetKey key;
/**
/**
* Spatial's parent, or null if it has none.
*/
protected transient Node parent;
@ -174,7 +176,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
/**
* Serialization only. Do not use.
* Not really. This class is never instantiated directly but the
* subclasses like to use the no-arg constructor for their own
* subclasses like to use the no-arg constructor for their own
* no-arg constructor... which is technically weaker than
* forward supplying defaults.
*/
@ -192,7 +194,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/
protected Spatial(String name) {
this.name = name;
localTransform = new Transform();
worldTransform = new Transform();
@ -219,13 +221,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
boolean requiresUpdates() {
return requiresUpdates | !controls.isEmpty();
}
/**
* Subclasses can call this with true to denote that they require
* Subclasses can call this with true to denote that they require
* updateLogicalState() to be called even if they contain no controls.
* Setting this to false reverts to the default behavior of only
* updating if the spatial has controls. This is not meant to
* indicate dynamic state in any way and must be called while
* indicate dynamic state in any way and must be called while
* unattached or an IllegalStateException is thrown. It is designed
* to be called during object construction and then never changed, ie:
* it's meant to be subclass specific state and not runtime state.
@ -251,12 +253,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
// override it for more optimal behavior. Node and Geometry will override
// it to false if the class is Node.class or Geometry.class.
// This means that all subclasses will default to the old behavior
// unless they opt in.
// unless they opt in.
if( parent != null ) {
throw new IllegalStateException("setRequiresUpdates() cannot be called once attached.");
throw new IllegalStateException("setRequiresUpdates() cannot be called once attached.");
}
this.requiresUpdates = f;
}
}
/**
* Indicate that the transform of this spatial has changed and that
@ -269,13 +271,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
protected void setLightListRefresh() {
refreshFlags |= RF_LIGHTLIST;
// Make sure next updateGeometricState() visits this branch
// to update lights.
Spatial p = parent;
while (p != null) {
//if (p.refreshFlags != 0) {
// any refresh flag is sufficient,
// any refresh flag is sufficient,
// as each propagates to the root Node
// 2015/2/8:
@ -283,16 +285,16 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
// or getWorldTransform() activates a "partial refresh"
// which does not update the lights but does clear
// the refresh flags on the ancestors!
// return;
// return;
//}
if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
// The parent already has this flag,
// so must all ancestors.
return;
}
p.refreshFlags |= RF_CHILD_LIGHTLIST;
p = p.parent;
}
@ -315,10 +317,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
p = p.parent;
}
}
/**
* (Internal use only) Forces a refresh of the given types of data.
*
*
* @param transforms Refresh world transform based on parents'
* @param bounds Refresh bounding volume data based on child nodes
* @param lights Refresh light list based on parents'
@ -401,9 +403,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
/**
* Returns the local {@link LightList}, which are the lights
* that were directly attached to this <code>Spatial</code> 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 <code>Spatial's</code> parents up to and including
* this <code>Spatial</code>'s lights.
*
*
* @return The combined world light list
*/
public LightList getWorldLightList() {
@ -502,14 +504,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* <code>lookAt</code> 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 <T extends Control> T getControl(Class<T> controlType) {
for (Control c : controls.getArray()) {
@ -790,7 +792,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
/**
* @return The number of controls attached to this Spatial.
* @see Spatial#addControl(com.jme3.scene.control.Control)
* @see Spatial#removeControl(java.lang.Class)
* @see Spatial#removeControl(java.lang.Class)
*/
public int getNumControls() {
return controls.size();
@ -815,7 +817,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* Calling this when the Spatial is attached to a node
* will cause undefined results. User code should only call this
* method on Spatials having no parent.
*
*
* @see Spatial#getWorldLightList()
* @see Spatial#getWorldTransform()
* @see Spatial#getWorldBound()
@ -835,7 +837,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
if ((refreshFlags & RF_BOUND) != 0) {
updateWorldBound();
}
assert refreshFlags == 0;
}
@ -1067,9 +1069,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
/**
* <code>removeLight</code> 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 = (HashMap<String, Savable>)userData.clone();
for( Map.Entry<String, Savable> e : userData.entrySet() ) {
Savable value = e.getValue();
if( value instanceof Cloneable ) {
// Note: all JmeCloneable objects are also Cloneable so this
// catches both cases.
e.setValue(cloner.clone(value));
}
}
}
public void setUserData(String key, Object data) {
if (userData == null) {
userData = new HashMap<String, Savable>();
}
if(data == null){
userData.remove(key);
userData.remove(key);
}else if (data instanceof Savable) {
userData.put(key, (Savable) data);
} else {
@ -1445,7 +1493,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
//the AnimControl creates the SkeletonControl for old files and add it to the spatial.
//The SkeletonControl must be the last in the stack so we add the list of all other control before it.
//When backward compatibility won't be needed anymore this can be replaced by :
//When backward compatibility won't be needed anymore this can be replaced by :
//controls = ic.readSavableArrayList("controlsList", null));
controls.addAll(0, ic.readSavableArrayList("controlsList", null));
@ -1508,9 +1556,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
/**
* <code>setQueueBucket</code> 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) {

@ -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 <code>null</code>, 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 <code>null</code>, 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<VertexBuffer> allData = new ArrayList();
if (transformInstanceData != null) {
@ -343,6 +344,16 @@ public class InstancedGeometry extends Geometry {
return allData.toArray(new VertexBuffer[allData.size()]);
}
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.globalInstanceData = cloner.clone(globalInstanceData);
this.transformInstanceData = cloner.clone(transformInstanceData);
this.geometries = cloner.clone(geometries);
}
@Override
public void write(JmeExporter exporter) throws IOException {
super.write(exporter);
@ -350,7 +361,7 @@ public class InstancedGeometry extends Geometry {
//capsule.write(currentNumInstances, "cur_num_instances", 1);
capsule.write(geometries, "geometries", null);
}
@Override
public void read(JmeImporter importer) throws IOException {
super.read(importer);

@ -48,18 +48,19 @@ import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class InstancedNode extends GeometryGroupNode {
static int getGeometryStartIndex2(Geometry geom) {
return getGeometryStartIndex(geom);
}
static void setGeometryStartIndex2(Geometry geom, int startIndex) {
setGeometryStartIndex(geom, startIndex);
}
private static final class InstanceTypeKey implements Cloneable {
private static final class InstanceTypeKey implements Cloneable, JmeCloneable {
Mesh mesh;
Material material;
@ -70,7 +71,7 @@ public class InstancedNode extends GeometryGroupNode {
this.material = material;
this.lodLevel = lodLevel;
}
public InstanceTypeKey(){
}
@ -97,7 +98,7 @@ public class InstancedNode extends GeometryGroupNode {
}
return true;
}
@Override
public InstanceTypeKey clone() {
try {
@ -106,26 +107,41 @@ public class InstancedNode extends GeometryGroupNode {
throw new AssertionError();
}
}
@Override
public Object jmeClone() {
try {
return super.clone();
} catch( CloneNotSupportedException e ) {
throw new AssertionError();
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.mesh = cloner.clone(mesh);
this.material = cloner.clone(material);
}
}
private static class InstancedNodeControl implements Control, JmeCloneable {
private InstancedNode node;
public InstancedNodeControl() {
}
public InstancedNodeControl(InstancedNode node) {
this.node = node;
}
@Override
public Control cloneForSpatial(Spatial spatial) {
return this;
return this;
// WARNING: Sets wrong control on spatial. Will be
// fixed automatically by InstancedNode.clone() method.
}
@Override
public Object jmeClone() {
try {
@ -133,52 +149,52 @@ public class InstancedNode extends GeometryGroupNode {
} catch( CloneNotSupportedException e ) {
throw new RuntimeException("Error cloning control", e);
}
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
public void cloneFields( Cloner cloner, Object original ) {
this.node = cloner.clone(node);
}
public void setSpatial(Spatial spatial){
}
public void update(float tpf){
}
public void render(RenderManager rm, ViewPort vp) {
node.renderFromControl();
}
public void write(JmeExporter ex) throws IOException {
}
public void read(JmeImporter im) throws IOException {
}
}
protected InstancedNodeControl control;
protected HashMap<Geometry, InstancedGeometry> igByGeom
protected HashMap<Geometry, InstancedGeometry> igByGeom
= new HashMap<Geometry, InstancedGeometry>();
private InstanceTypeKey lookUp = new InstanceTypeKey();
private HashMap<InstanceTypeKey, InstancedGeometry> instancesMap =
private HashMap<InstanceTypeKey, InstancedGeometry> instancesMap =
new HashMap<InstanceTypeKey, InstancedGeometry>();
public InstancedNode() {
super();
// NOTE: since we are deserializing,
// the control is going to be added automatically here.
}
public InstancedNode(String name) {
super(name);
control = new InstancedNodeControl(this);
addControl(control);
}
private void renderFromControl() {
for (InstancedGeometry ig : instancesMap.values()) {
ig.updateInstances();
@ -207,7 +223,7 @@ public class InstancedNode extends GeometryGroupNode {
return ig;
}
private void addToInstancedGeometry(Geometry geom) {
Material material = geom.getMaterial();
MatParam param = material.getParam("UseInstancing");
@ -216,20 +232,20 @@ public class InstancedNode extends GeometryGroupNode {
+ "parameter to true on the material prior "
+ "to adding it to InstancedNode");
}
InstancedGeometry ig = lookUpByGeometry(geom);
igByGeom.put(geom, ig);
geom.associateWithGroupNode(this, 0);
ig.addInstance(geom);
}
private void removeFromInstancedGeometry(Geometry geom) {
InstancedGeometry ig = igByGeom.remove(geom);
if (ig != null) {
ig.deleteInstance(geom);
}
}
private void relocateInInstancedGeometry(Geometry geom) {
InstancedGeometry oldIG = igByGeom.get(geom);
InstancedGeometry newIG = lookUpByGeometry(geom);
@ -242,7 +258,7 @@ public class InstancedNode extends GeometryGroupNode {
igByGeom.put(geom, newIG);
}
}
private void ungroupSceneGraph(Spatial s) {
if (s instanceof Node) {
for (Spatial sp : ((Node) s).getChildren()) {
@ -253,14 +269,14 @@ public class InstancedNode extends GeometryGroupNode {
if (g.isGrouped()) {
// Will invoke onGeometryUnassociated automatically.
g.unassociateFromGroupNode();
if (InstancedNode.getGeometryStartIndex(g) != -1) {
throw new AssertionError();
}
}
}
}
@Override
public Spatial detachChildAt(int index) {
Spatial s = super.detachChildAt(index);
@ -269,7 +285,7 @@ public class InstancedNode extends GeometryGroupNode {
}
return s;
}
private void instance(Spatial n) {
if (n instanceof Geometry) {
Geometry g = (Geometry) n;
@ -285,20 +301,20 @@ public class InstancedNode extends GeometryGroupNode {
}
}
}
public void instance() {
instance(this);
}
@Override
public Node clone() {
return clone(true);
}
@Override
public Node clone(boolean cloneMaterials) {
InstancedNode clone = (InstancedNode)super.clone(cloneMaterials);
if (instancesMap.size() > 0) {
// Remove all instanced geometries from the clone
for (int i = 0; i < clone.children.size(); i++) {
@ -312,7 +328,7 @@ public class InstancedNode extends GeometryGroupNode {
}
}
}
// remove original control from the clone
clone.controls.remove(this.control);
@ -323,12 +339,33 @@ public class InstancedNode extends GeometryGroupNode {
clone.lookUp = new InstanceTypeKey();
clone.igByGeom = new HashMap<Geometry, InstancedGeometry>();
clone.instancesMap = new HashMap<InstanceTypeKey, InstancedGeometry>();
clone.instance();
return clone;
}
/**
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
*/
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.control = cloner.clone(control);
this.lookUp = cloner.clone(lookUp);
HashMap<Geometry, InstancedGeometry> newIgByGeom = new HashMap<Geometry, InstancedGeometry>();
for( Map.Entry<Geometry, InstancedGeometry> e : igByGeom.entrySet() ) {
newIgByGeom.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
}
this.igByGeom = newIgByGeom;
HashMap<InstanceTypeKey, InstancedGeometry> newInstancesMap = new HashMap<InstanceTypeKey, InstancedGeometry>();
for( Map.Entry<InstanceTypeKey, InstancedGeometry> e : instancesMap.entrySet() ) {
newInstancesMap.put(cloner.clone(e.getKey()), cloner.clone(e.getValue()));
}
this.instancesMap = newInstancesMap;
}
@Override
public void onTransformChange(Geometry geom) {
// Handled automatically

@ -32,19 +32,21 @@
package com.jme3.util;
import com.jme3.util.IntMap.Entry;
import com.jme3.util.clone.Cloner;
import com.jme3.util.clone.JmeCloneable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* Similar to a {@link Map} except that ints are used as keys.
*
*
* Taken from <a href="http://code.google.com/p/skorpios/">http://code.google.com/p/skorpios/</a>
*
* @author Nate
*
* @author Nate
*/
public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable, JmeCloneable {
private Entry[] table;
private final float loadFactor;
private int size, mask, capacity, threshold;
@ -93,6 +95,26 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
return null;
}
/**
* 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.table = cloner.clone(table);
}
public boolean containsValue(Object value) {
Entry[] table = this.table;
for (int i = table.length; i-- > 0;){
@ -228,7 +250,7 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
idx = 0;
el = 0;
}
public boolean hasNext() {
return el < size;
}
@ -255,20 +277,20 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
// the entry was null. find another non-null entry.
cur = table[++idx];
} while (cur == null);
Entry e = cur;
cur = cur.next;
el ++;
return e;
}
public void remove() {
}
}
public static final class Entry<T> implements Cloneable {
public static final class Entry<T> implements Cloneable, JmeCloneable {
final int key;
T value;
@ -303,5 +325,20 @@ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable {
}
return null;
}
@Override
public Object jmeClone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new AssertionError();
}
}
@Override
public void cloneFields( Cloner cloner, Object original ) {
this.value = cloner.clone(value);
this.next = cloner.clone(next);
}
}
}

@ -1,11 +1,12 @@
# THIS IS AN AUTO-GENERATED FILE..
# DO NOT MODIFY!
build.date=1900-01-01
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
Loading…
Cancel
Save