|
@@ -38,6 +38,7 @@ import com.jme3.collision.Collidable;
|
|
|
import com.jme3.export.*;
|
|
|
import com.jme3.light.Light;
|
|
|
import com.jme3.light.LightList;
|
|
|
+import com.jme3.material.MatParamOverride;
|
|
|
import com.jme3.material.Material;
|
|
|
import com.jme3.math.*;
|
|
|
import com.jme3.renderer.Camera;
|
|
@@ -47,6 +48,9 @@ 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.IdentityCloneFunction;
|
|
|
+import com.jme3.util.clone.JmeCloneable;
|
|
|
import com.jme3.util.SafeArrayList;
|
|
|
import com.jme3.util.TempVars;
|
|
|
import java.io.IOException;
|
|
@@ -63,17 +67,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 +87,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 +104,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;
|
|
@@ -119,11 +123,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_CHILD_LIGHTLIST = 0x08; // some child need geometry update
|
|
|
+ RF_CHILD_LIGHTLIST = 0x08, // some child need geometry update
|
|
|
+ RF_MATPARAM_OVERRIDE = 0x10;
|
|
|
|
|
|
protected CullHint cullHint = CullHint.Inherit;
|
|
|
protected BatchHint batchHint = BatchHint.Inherit;
|
|
|
- /**
|
|
|
+ /**
|
|
|
* Spatial's bounding volume relative to the world.
|
|
|
*/
|
|
|
protected BoundingVolume worldBound;
|
|
@@ -132,6 +137,10 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
*/
|
|
|
protected LightList localLights;
|
|
|
protected transient LightList worldLights;
|
|
|
+
|
|
|
+ protected ArrayList<MatParamOverride> localOverrides;
|
|
|
+ protected ArrayList<MatParamOverride> worldOverrides;
|
|
|
+
|
|
|
/**
|
|
|
* This spatial's name.
|
|
|
*/
|
|
@@ -147,11 +156,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 +183,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,13 +201,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
*/
|
|
|
protected Spatial(String name) {
|
|
|
this.name = name;
|
|
|
-
|
|
|
localTransform = new Transform();
|
|
|
worldTransform = new Transform();
|
|
|
|
|
|
localLights = new LightList(this);
|
|
|
worldLights = new LightList(this);
|
|
|
|
|
|
+ localOverrides = new ArrayList<>();
|
|
|
+ worldOverrides = new ArrayList<>();
|
|
|
refreshFlags |= RF_BOUND;
|
|
|
}
|
|
|
|
|
@@ -219,13 +229,12 @@ 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 +260,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,35 +278,33 @@ 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,
|
|
|
- // as each propagates to the root Node
|
|
|
-
|
|
|
- // 2015/2/8:
|
|
|
- // This is not true, because using e.g. getWorldBound()
|
|
|
- // or getWorldTransform() activates a "partial refresh"
|
|
|
- // which does not update the lights but does clear
|
|
|
- // the refresh flags on the ancestors!
|
|
|
-
|
|
|
- // 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;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ protected void setMatParamOverrideRefresh() {
|
|
|
+ refreshFlags |= RF_MATPARAM_OVERRIDE;
|
|
|
+ Spatial p = parent;
|
|
|
+ while (p != null) {
|
|
|
+ if ((p.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ p.refreshFlags |= RF_MATPARAM_OVERRIDE;
|
|
|
+ p = p.parent;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Indicate that the bounding of this spatial has changed and that
|
|
|
* a refresh is required.
|
|
@@ -315,10 +322,9 @@ 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 +407,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,13 +420,36 @@ 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() {
|
|
|
return worldLights;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Get the local material parameter overrides.
|
|
|
+ *
|
|
|
+ * @return The list of local material parameter overrides.
|
|
|
+ */
|
|
|
+ public List<MatParamOverride> getLocalMatParamOverrides() {
|
|
|
+ return localOverrides;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the world material parameter overrides.
|
|
|
+ *
|
|
|
+ * Note that this list is only updated on a call to
|
|
|
+ * {@link #updateGeometricState()}. After update, the world overrides list
|
|
|
+ * will contain the {@link #getParent() parent's} world overrides combined
|
|
|
+ * with this spatial's {@link #getLocalMatParamOverrides() local overrides}.
|
|
|
+ *
|
|
|
+ * @return The list of world material parameter overrides.
|
|
|
+ */
|
|
|
+ public List<MatParamOverride> getWorldMatParamOverrides() {
|
|
|
+ return worldOverrides;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* <code>getWorldRotation</code> retrieves the absolute rotation of the
|
|
|
* Spatial.
|
|
@@ -502,14 +531,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 +551,8 @@ 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());
|
|
@@ -552,15 +579,63 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
worldLights.update(localLights, null);
|
|
|
refreshFlags &= ~RF_LIGHTLIST;
|
|
|
} else {
|
|
|
- if ((parent.refreshFlags & RF_LIGHTLIST) == 0) {
|
|
|
- worldLights.update(localLights, parent.worldLights);
|
|
|
- refreshFlags &= ~RF_LIGHTLIST;
|
|
|
- } else {
|
|
|
- assert false;
|
|
|
- }
|
|
|
+ assert (parent.refreshFlags & RF_LIGHTLIST) == 0;
|
|
|
+ worldLights.update(localLights, parent.worldLights);
|
|
|
+ refreshFlags &= ~RF_LIGHTLIST;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void updateMatParamOverrides() {
|
|
|
+ refreshFlags &= ~RF_MATPARAM_OVERRIDE;
|
|
|
+
|
|
|
+ worldOverrides.clear();
|
|
|
+ if (parent == null) {
|
|
|
+ worldOverrides.addAll(localOverrides);
|
|
|
+ } else {
|
|
|
+ assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0;
|
|
|
+ worldOverrides.addAll(localOverrides);
|
|
|
+ worldOverrides.addAll(parent.worldOverrides);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Adds a local material parameter override.
|
|
|
+ *
|
|
|
+ * @param override The override to add.
|
|
|
+ * @see MatParamOverride
|
|
|
+ */
|
|
|
+ public void addMatParamOverride(MatParamOverride override) {
|
|
|
+ if (override == null) {
|
|
|
+ throw new IllegalArgumentException("override cannot be null");
|
|
|
+ }
|
|
|
+ localOverrides.add(override);
|
|
|
+ setMatParamOverrideRefresh();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Remove a local material parameter override if it exists.
|
|
|
+ *
|
|
|
+ * @param override The override to remove.
|
|
|
+ * @see MatParamOverride
|
|
|
+ */
|
|
|
+ public void removeMatParamOverride(MatParamOverride override) {
|
|
|
+ if (localOverrides.remove(override)) {
|
|
|
+ setMatParamOverrideRefresh();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Remove all local material parameter overrides.
|
|
|
+ *
|
|
|
+ * @see #addMatParamOverride(com.jme3.material.MatParamOverride)
|
|
|
+ */
|
|
|
+ public void clearMatParamOverrides() {
|
|
|
+ if (!localOverrides.isEmpty()) {
|
|
|
+ setMatParamOverrideRefresh();
|
|
|
+ }
|
|
|
+ localOverrides.clear();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Should only be called from updateGeometricState().
|
|
|
* In most cases should not be subclassed.
|
|
@@ -579,7 +654,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 +745,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 +761,25 @@ 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 +791,22 @@ 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 +816,12 @@ 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 +832,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 +861,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 +886,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,6 +906,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
if ((refreshFlags & RF_BOUND) != 0) {
|
|
|
updateWorldBound();
|
|
|
}
|
|
|
+ if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
|
|
+ updateMatParamOverrides();
|
|
|
+ }
|
|
|
|
|
|
assert refreshFlags == 0;
|
|
|
}
|
|
@@ -1067,9 +1141,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);
|
|
@@ -1261,12 +1335,43 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
* Note that meshes of geometries are not cloned explicitly, they
|
|
|
* are shared if static, or specially cloned if animated.
|
|
|
*
|
|
|
- * All controls will be cloned using the Control.cloneForSpatial method
|
|
|
- * on the clone.
|
|
|
- *
|
|
|
- * @see Mesh#cloneForAnim()
|
|
|
+ * @see Mesh#cloneForAnim()
|
|
|
+ */
|
|
|
+ public Spatial clone( boolean cloneMaterial ) {
|
|
|
+
|
|
|
+ // Setup the cloner for the type of cloning we want to do.
|
|
|
+ Cloner cloner = new Cloner();
|
|
|
+
|
|
|
+ // First, we definitely do not want to clone our own parent
|
|
|
+ cloner.setClonedValue(parent, null);
|
|
|
+
|
|
|
+ // If we aren't cloning materials then we will make sure those
|
|
|
+ // aren't cloned also
|
|
|
+ if( !cloneMaterial ) {
|
|
|
+ cloner.setCloneFunction(Material.class, new IdentityCloneFunction<Material>());
|
|
|
+ }
|
|
|
+
|
|
|
+ // By default the meshes are not cloned. The geometry
|
|
|
+ // may choose to selectively force them to be cloned but
|
|
|
+ // normally they will be shared
|
|
|
+ cloner.setCloneFunction(Mesh.class, new IdentityCloneFunction<Mesh>());
|
|
|
+
|
|
|
+ // Clone it!
|
|
|
+ Spatial clone = cloner.clone(this);
|
|
|
+
|
|
|
+ // Because we've nulled the parent out we need to make sure
|
|
|
+ // the transforms and stuff get refreshed.
|
|
|
+ clone.setTransformRefresh();
|
|
|
+ clone.setLightListRefresh();
|
|
|
+ clone.setMatParamOverrideRefresh();
|
|
|
+
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The old clone() method that did not use the new Cloner utility.
|
|
|
*/
|
|
|
- public Spatial clone(boolean cloneMaterial) {
|
|
|
+ public Spatial oldClone(boolean cloneMaterial) {
|
|
|
try {
|
|
|
Spatial clone = (Spatial) super.clone();
|
|
|
if (worldBound != null) {
|
|
@@ -1279,6 +1384,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
clone.localLights.setOwner(clone);
|
|
|
clone.worldLights.setOwner(clone);
|
|
|
|
|
|
+ clone.worldOverrides = new ArrayList<MatParamOverride>();
|
|
|
+ clone.localOverrides = new ArrayList<MatParamOverride>();
|
|
|
+
|
|
|
+ for (MatParamOverride override : localOverrides) {
|
|
|
+ clone.localOverrides.add((MatParamOverride) override.clone());
|
|
|
+ }
|
|
|
+
|
|
|
// No need to force cloned to update.
|
|
|
// This node already has the refresh flags
|
|
|
// set below so it will have to update anyway.
|
|
@@ -1300,6 +1412,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
clone.setBoundRefresh();
|
|
|
clone.setTransformRefresh();
|
|
|
clone.setLightListRefresh();
|
|
|
+ clone.setMatParamOverrideRefresh();
|
|
|
|
|
|
clone.controls = new SafeArrayList<Control>(Control.class);
|
|
|
for (int i = 0; i < controls.size(); i++) {
|
|
@@ -1328,7 +1441,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() {
|
|
@@ -1342,7 +1455,73 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
*
|
|
|
* @see Spatial#clone()
|
|
|
*/
|
|
|
- public abstract Spatial deepClone();
|
|
|
+ public Spatial deepClone() {
|
|
|
+ // Setup the cloner for the type of cloning we want to do.
|
|
|
+ Cloner cloner = new Cloner();
|
|
|
+
|
|
|
+ // First, we definitely do not want to clone our own parent
|
|
|
+ cloner.setClonedValue(parent, null);
|
|
|
+
|
|
|
+ Spatial clone = cloner.clone(this);
|
|
|
+
|
|
|
+ // Because we've nulled the parent out we need to make sure
|
|
|
+ // the transforms and stuff get refreshed.
|
|
|
+ clone.setTransformRefresh();
|
|
|
+ clone.setLightListRefresh();
|
|
|
+ clone.setMatParamOverrideRefresh();
|
|
|
+
|
|
|
+ return clone;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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.worldOverrides = cloner.clone(worldOverrides);
|
|
|
+ this.localOverrides = cloner.clone(localOverrides);
|
|
|
+ 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.
|
|
|
+ if( userData != null ) {
|
|
|
+ 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) {
|
|
@@ -1350,7 +1529,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
}
|
|
|
|
|
|
if(data == null){
|
|
|
- userData.remove(key);
|
|
|
+ userData.remove(key);
|
|
|
}else if (data instanceof Savable) {
|
|
|
userData.put(key, (Savable) data);
|
|
|
} else {
|
|
@@ -1419,6 +1598,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
|
|
|
capsule.write(localTransform, "transform", Transform.IDENTITY);
|
|
|
capsule.write(localLights, "lights", null);
|
|
|
+ capsule.writeSavableArrayList(localOverrides, "overrides", null);
|
|
|
|
|
|
// Shallow clone the controls array to convert its type.
|
|
|
capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
|
|
@@ -1442,10 +1622,16 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|
|
localLights = (LightList) ic.readSavable("lights", null);
|
|
|
localLights.setOwner(this);
|
|
|
|
|
|
+ localOverrides = ic.readSavableArrayList("overrides", null);
|
|
|
+ if (localOverrides == null) {
|
|
|
+ localOverrides = new ArrayList<>();
|
|
|
+ }
|
|
|
+ worldOverrides = new ArrayList<>();
|
|
|
+
|
|
|
//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 +1694,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 +1781,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) {
|