MPO: implement propagation and add test
Conflicts: jme3-core/src/main/java/com/jme3/scene/Node.java jme3-core/src/main/java/com/jme3/scene/Spatial.java
This commit is contained in:
parent
280733c1ce
commit
2b35f288c2
@ -35,6 +35,10 @@ import com.jme3.shader.VarType;
|
|||||||
|
|
||||||
public final class MatParamOverride extends MatParam {
|
public final class MatParamOverride extends MatParam {
|
||||||
|
|
||||||
|
public MatParamOverride() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public MatParamOverride(VarType type, String name, Object value) {
|
public MatParamOverride(VarType type, String name, Object value) {
|
||||||
super(type, name, value);
|
super(type, name, value);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,6 @@ public class Node extends Spatial {
|
|||||||
* requiresUpdate() method.
|
* requiresUpdate() method.
|
||||||
*/
|
*/
|
||||||
private SafeArrayList<Spatial> updateList = null;
|
private SafeArrayList<Spatial> updateList = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* False if the update list requires rebuilding. This is Node.class
|
* False if the update list requires rebuilding. This is Node.class
|
||||||
* specific and therefore not included as part of the Spatial update flags.
|
* specific and therefore not included as part of the Spatial update flags.
|
||||||
@ -100,7 +99,6 @@ public class Node extends Spatial {
|
|||||||
*/
|
*/
|
||||||
public Node(String name) {
|
public Node(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
|
|
||||||
// For backwards compatibility, only clear the "requires
|
// For backwards compatibility, only clear the "requires
|
||||||
// update" flag if we are not a subclass of Node.
|
// update" flag if we are not a subclass of Node.
|
||||||
// This prevents subclass from silently failing to receive
|
// This prevents subclass from silently failing to receive
|
||||||
@ -141,10 +139,21 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setMatParamOverrideRefresh() {
|
||||||
|
super.setMatParamOverrideRefresh();
|
||||||
|
for (Spatial child : children.getArray()) {
|
||||||
|
if ((child.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
child.setMatParamOverrideRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateWorldBound(){
|
protected void updateWorldBound(){
|
||||||
super.updateWorldBound();
|
super.updateWorldBound();
|
||||||
|
|
||||||
// for a node, the world bound is a combination of all it's children
|
// for a node, the world bound is a combination of all it's children
|
||||||
// bounds
|
// bounds
|
||||||
BoundingVolume resultBound = null;
|
BoundingVolume resultBound = null;
|
||||||
@ -239,11 +248,13 @@ public class Node extends Spatial {
|
|||||||
// This branch has no geometric state that requires updates.
|
// This branch has no geometric state that requires updates.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((refreshFlags & RF_LIGHTLIST) != 0){
|
if ((refreshFlags & RF_LIGHTLIST) != 0){
|
||||||
updateWorldLightList();
|
updateWorldLightList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||||
|
updateMatParamOverrides();
|
||||||
|
}
|
||||||
if ((refreshFlags & RF_TRANSFORM) != 0){
|
if ((refreshFlags & RF_TRANSFORM) != 0){
|
||||||
// combine with parent transforms- same for all spatial
|
// combine with parent transforms- same for all spatial
|
||||||
// subclasses.
|
// subclasses.
|
||||||
@ -251,7 +262,6 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshFlags &= ~RF_CHILD_LIGHTLIST;
|
refreshFlags &= ~RF_CHILD_LIGHTLIST;
|
||||||
|
|
||||||
if (!children.isEmpty()) {
|
if (!children.isEmpty()) {
|
||||||
// the important part- make sure child geometric state is refreshed
|
// the important part- make sure child geometric state is refreshed
|
||||||
// first before updating own world bound. This saves
|
// first before updating own world bound. This saves
|
||||||
@ -287,7 +297,6 @@ public class Node extends Spatial {
|
|||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>getVertexCount</code> returns the number of vertices contained
|
* <code>getVertexCount</code> returns the number of vertices contained
|
||||||
* in all sub-branches of this node that contain geometry.
|
* in all sub-branches of this node that contain geometry.
|
||||||
@ -321,7 +330,6 @@ public class Node extends Spatial {
|
|||||||
public int attachChild(Spatial child) {
|
public int attachChild(Spatial child) {
|
||||||
return attachChildAt(child, children.size());
|
return attachChildAt(child, children.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* <code>attachChildAt</code> attaches a child to this node at an index. This node
|
* <code>attachChildAt</code> attaches a child to this node at an index. This node
|
||||||
@ -345,20 +353,18 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
child.setParent(this);
|
child.setParent(this);
|
||||||
children.add(index, child);
|
children.add(index, child);
|
||||||
|
|
||||||
// XXX: Not entirely correct? Forces bound update up the
|
// XXX: Not entirely correct? Forces bound update up the
|
||||||
// tree stemming from the attached child. Also forces
|
// tree stemming from the attached child. Also forces
|
||||||
// transform update down the tree-
|
// transform update down the tree-
|
||||||
child.setTransformRefresh();
|
child.setTransformRefresh();
|
||||||
child.setLightListRefresh();
|
child.setLightListRefresh();
|
||||||
|
child.setMatParamOverrideRefresh();
|
||||||
if (logger.isLoggable(Level.FINE)) {
|
if (logger.isLoggable(Level.FINE)) {
|
||||||
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
|
logger.log(Level.FINE,"Child ({0}) attached to this node ({1})",
|
||||||
new Object[]{child.getName(), getName()});
|
new Object[]{child.getName(), getName()});
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidateUpdateList();
|
invalidateUpdateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return children.size();
|
return children.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +439,7 @@ public class Node extends Spatial {
|
|||||||
child.setTransformRefresh();
|
child.setTransformRefresh();
|
||||||
// lights are also inherited from parent
|
// lights are also inherited from parent
|
||||||
child.setLightListRefresh();
|
child.setLightListRefresh();
|
||||||
|
child.setMatParamOverrideRefresh();
|
||||||
|
|
||||||
invalidateUpdateList();
|
invalidateUpdateList();
|
||||||
}
|
}
|
||||||
@ -519,7 +526,6 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* determines if the provided Spatial is contained in the children list of
|
* determines if the provided Spatial is contained in the children list of
|
||||||
* this node.
|
* this node.
|
||||||
@ -567,39 +573,32 @@ public class Node extends Spatial {
|
|||||||
|
|
||||||
public int collideWith(Collidable other, CollisionResults results){
|
public int collideWith(Collidable other, CollisionResults results){
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
// optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
|
// 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.
|
// 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
|
I'm removing this change until some issues can be addressed and I really
|
||||||
think it needs to be implemented a better way anyway.
|
think it needs to be implemented a better way anyway.
|
||||||
|
|
||||||
First, it causes issues for anyone doing collideWith() with BoundingVolumes
|
First, it causes issues for anyone doing collideWith() with BoundingVolumes
|
||||||
and expecting it to trickle down to the children. For example, children
|
and expecting it to trickle down to the children. For example, children
|
||||||
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
|
with BoundingSphere bounding volumes and collideWith(BoundingSphere). Doing
|
||||||
a collision check at the parent level then has to do a BoundingSphere to BoundingBox
|
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
|
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.)
|
case is tricky and the first sign that this is the wrong approach.)
|
||||||
|
|
||||||
Second, the rippling changes this caused to 'optimize' collideWith() for this
|
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
|
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
|
idea of calculating a full collision just to see if the two shapes collide at all
|
||||||
is very wasteful.
|
is very wasteful.
|
||||||
|
|
||||||
A proper implementation should support a simpler boolean check that doesn't do
|
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%
|
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
|
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.
|
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'
|
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
|
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
|
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
|
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.
|
enough to do all the time for > 1.
|
||||||
|
|
||||||
if (children.size() > 4)
|
if (children.size() > 4)
|
||||||
{
|
{
|
||||||
BoundingVolume bv = this.getWorldBound();
|
BoundingVolume bv = this.getWorldBound();
|
||||||
@ -692,7 +691,6 @@ public class Node extends Spatial {
|
|||||||
// Reset the fields of the clone that should be in a 'new' state.
|
// Reset the fields of the clone that should be in a 'new' state.
|
||||||
nodeClone.updateList = null;
|
nodeClone.updateList = null;
|
||||||
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
|
nodeClone.updateListValid = false; // safe because parent is nulled out in super.clone()
|
||||||
|
|
||||||
return nodeClone;
|
return nodeClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -732,7 +730,6 @@ public class Node extends Spatial {
|
|||||||
// cloning this list is fine.
|
// cloning this list is fine.
|
||||||
this.updateList = cloner.clone(updateList);
|
this.updateList = cloner.clone(updateList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter e) throws IOException {
|
public void write(JmeExporter e) throws IOException {
|
||||||
super.write(e);
|
super.write(e);
|
||||||
@ -744,7 +741,6 @@ public class Node extends Spatial {
|
|||||||
// XXX: Load children before loading itself!!
|
// XXX: Load children before loading itself!!
|
||||||
// This prevents empty children list if controls query
|
// This prevents empty children list if controls query
|
||||||
// it in Control.setSpatial().
|
// it in Control.setSpatial().
|
||||||
|
|
||||||
children = new SafeArrayList( Spatial.class,
|
children = new SafeArrayList( Spatial.class,
|
||||||
e.getCapsule(this).readSavableArrayList("children", null) );
|
e.getCapsule(this).readSavableArrayList("children", null) );
|
||||||
|
|
||||||
@ -754,7 +750,6 @@ public class Node extends Spatial {
|
|||||||
child.parent = this;
|
child.parent = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.read(e);
|
super.read(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,7 +770,6 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void depthFirstTraversal(SceneGraphVisitor visitor) {
|
public void depthFirstTraversal(SceneGraphVisitor visitor) {
|
||||||
for (Spatial child : children.getArray()) {
|
for (Spatial child : children.getArray()) {
|
||||||
@ -783,7 +777,6 @@ public class Node extends Spatial {
|
|||||||
}
|
}
|
||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
|
protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue<Spatial> queue) {
|
||||||
queue.addAll(children);
|
queue.addAll(children);
|
||||||
|
@ -123,7 +123,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
|
||||||
RF_BOUND = 0x02,
|
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
|
RF_CHILD_LIGHTLIST = 0x08, // some child need geometry update
|
||||||
|
RF_MATPARAM_OVERRIDE = 0x10;
|
||||||
|
|
||||||
protected CullHint cullHint = CullHint.Inherit;
|
protected CullHint cullHint = CullHint.Inherit;
|
||||||
protected BatchHint batchHint = BatchHint.Inherit;
|
protected BatchHint batchHint = BatchHint.Inherit;
|
||||||
@ -136,6 +137,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
*/
|
*/
|
||||||
protected LightList localLights;
|
protected LightList localLights;
|
||||||
protected transient LightList worldLights;
|
protected transient LightList worldLights;
|
||||||
|
protected ArrayList<MatParamOverride> localOverrides;
|
||||||
|
protected ArrayList<MatParamOverride> worldOverrides;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This spatial's name.
|
* This spatial's name.
|
||||||
*/
|
*/
|
||||||
@ -196,13 +200,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
*/
|
*/
|
||||||
protected Spatial(String name) {
|
protected Spatial(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
localTransform = new Transform();
|
localTransform = new Transform();
|
||||||
worldTransform = new Transform();
|
worldTransform = new Transform();
|
||||||
|
|
||||||
localLights = new LightList(this);
|
localLights = new LightList(this);
|
||||||
worldLights = new LightList(this);
|
worldLights = new LightList(this);
|
||||||
|
|
||||||
|
localOverrides = new ArrayList<MatParamOverride>();
|
||||||
|
worldOverrides = new ArrayList<MatParamOverride>();
|
||||||
refreshFlags |= RF_BOUND;
|
refreshFlags |= RF_BOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +228,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
boolean requiresUpdates() {
|
boolean requiresUpdates() {
|
||||||
return requiresUpdates | !controls.isEmpty();
|
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.
|
* updateLogicalState() to be called even if they contain no controls.
|
||||||
@ -273,35 +277,32 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
|
|
||||||
protected void setLightListRefresh() {
|
protected void setLightListRefresh() {
|
||||||
refreshFlags |= RF_LIGHTLIST;
|
refreshFlags |= RF_LIGHTLIST;
|
||||||
|
|
||||||
// Make sure next updateGeometricState() visits this branch
|
// Make sure next updateGeometricState() visits this branch
|
||||||
// to update lights.
|
// to update lights.
|
||||||
Spatial p = parent;
|
Spatial p = parent;
|
||||||
while (p != null) {
|
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) {
|
if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
|
||||||
// The parent already has this flag,
|
// The parent already has this flag,
|
||||||
// so must all ancestors.
|
// so must all ancestors.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.refreshFlags |= RF_CHILD_LIGHTLIST;
|
p.refreshFlags |= RF_CHILD_LIGHTLIST;
|
||||||
p = p.parent;
|
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
|
* Indicate that the bounding of this spatial has changed and that
|
||||||
* a refresh is required.
|
* a refresh is required.
|
||||||
@ -319,7 +320,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
p = p.parent;
|
p = p.parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (Internal use only) Forces a refresh of the given types of data.
|
* (Internal use only) Forces a refresh of the given types of data.
|
||||||
*
|
*
|
||||||
@ -431,7 +431,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
* @return The list of local material parameter overrides.
|
* @return The list of local material parameter overrides.
|
||||||
*/
|
*/
|
||||||
public ArrayList<MatParamOverride> getLocalOverrides() {
|
public ArrayList<MatParamOverride> getLocalOverrides() {
|
||||||
return null;
|
return localOverrides;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -445,7 +445,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
* @return The list of world material parameter overrides.
|
* @return The list of world material parameter overrides.
|
||||||
*/
|
*/
|
||||||
public ArrayList<MatParamOverride> getWorldOverrides() {
|
public ArrayList<MatParamOverride> getWorldOverrides() {
|
||||||
return null;
|
return worldOverrides;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -549,10 +549,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
Vector3f compVecA = vars.vect4;
|
Vector3f compVecA = vars.vect4;
|
||||||
|
|
||||||
compVecA.set(position).subtractLocal(worldTranslation);
|
compVecA.set(position).subtractLocal(worldTranslation);
|
||||||
getLocalRotation().lookAt(compVecA, upVector);
|
getLocalRotation().lookAt(compVecA, upVector);
|
||||||
|
|
||||||
if ( getParent() != null ) {
|
if ( getParent() != null ) {
|
||||||
Quaternion rot=vars.quat1;
|
Quaternion rot=vars.quat1;
|
||||||
rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
|
rot = rot.set(parent.getWorldRotation()).inverseLocal().multLocal(getLocalRotation());
|
||||||
@ -579,13 +577,47 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
worldLights.update(localLights, null);
|
worldLights.update(localLights, null);
|
||||||
refreshFlags &= ~RF_LIGHTLIST;
|
refreshFlags &= ~RF_LIGHTLIST;
|
||||||
} else {
|
} else {
|
||||||
if ((parent.refreshFlags & RF_LIGHTLIST) == 0) {
|
assert (parent.refreshFlags & RF_LIGHTLIST) == 0;
|
||||||
worldLights.update(localLights, parent.worldLights);
|
worldLights.update(localLights, parent.worldLights);
|
||||||
refreshFlags &= ~RF_LIGHTLIST;
|
refreshFlags &= ~RF_LIGHTLIST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateMatParamOverrides() {
|
||||||
|
refreshFlags &= ~RF_MATPARAM_OVERRIDE;
|
||||||
|
|
||||||
|
worldOverrides.clear();
|
||||||
|
if (parent == null) {
|
||||||
|
worldOverrides.addAll(localOverrides);
|
||||||
} else {
|
} else {
|
||||||
assert false;
|
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 #getLocalOverrides()
|
||||||
|
*/
|
||||||
|
public void addMatParamOverride(MatParamOverride override) {
|
||||||
|
localOverrides.add(override);
|
||||||
|
setMatParamOverrideRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeMatParamOverride(MatParamOverride override) {
|
||||||
|
if (worldOverrides.remove(override)) {
|
||||||
|
setMatParamOverrideRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearMatParamOverrides() {
|
||||||
|
if (!worldOverrides.isEmpty()) {
|
||||||
|
setMatParamOverrideRefresh();
|
||||||
|
}
|
||||||
|
worldOverrides.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -720,7 +752,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
controls.add(control);
|
controls.add(control);
|
||||||
control.setSpatial(this);
|
control.setSpatial(this);
|
||||||
boolean after = requiresUpdates();
|
boolean after = requiresUpdates();
|
||||||
|
|
||||||
// If the requirement to be updated has changed
|
// If the requirement to be updated has changed
|
||||||
// then we need to let the parent node know so it
|
// then we need to let the parent node know so it
|
||||||
// can rebuild its update list.
|
// can rebuild its update list.
|
||||||
@ -744,7 +775,6 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean after = requiresUpdates();
|
boolean after = requiresUpdates();
|
||||||
|
|
||||||
// If the requirement to be updated has changed
|
// If the requirement to be updated has changed
|
||||||
// then we need to let the parent node know so it
|
// then we need to let the parent node know so it
|
||||||
// can rebuild its update list.
|
// can rebuild its update list.
|
||||||
@ -770,14 +800,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean after = requiresUpdates();
|
boolean after = requiresUpdates();
|
||||||
|
|
||||||
// If the requirement to be updated has changed
|
// If the requirement to be updated has changed
|
||||||
// then we need to let the parent node know so it
|
// then we need to let the parent node know so it
|
||||||
// can rebuild its update list.
|
// can rebuild its update list.
|
||||||
if( parent != null && before != after ) {
|
if( parent != null && before != after ) {
|
||||||
parent.invalidateUpdateList();
|
parent.invalidateUpdateList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -862,6 +890,9 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
if ((refreshFlags & RF_BOUND) != 0) {
|
if ((refreshFlags & RF_BOUND) != 0) {
|
||||||
updateWorldBound();
|
updateWorldBound();
|
||||||
}
|
}
|
||||||
|
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
|
||||||
|
updateMatParamOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
assert refreshFlags == 0;
|
assert refreshFlags == 0;
|
||||||
}
|
}
|
||||||
@ -1336,6 +1367,8 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
clone.localLights.setOwner(clone);
|
clone.localLights.setOwner(clone);
|
||||||
clone.worldLights.setOwner(clone);
|
clone.worldLights.setOwner(clone);
|
||||||
|
|
||||||
|
clone.worldOverrides = new ArrayList<MatParamOverride>(worldOverrides);
|
||||||
|
clone.localOverrides = new ArrayList<MatParamOverride>(localOverrides);
|
||||||
// No need to force cloned to update.
|
// No need to force cloned to update.
|
||||||
// This node already has the refresh flags
|
// This node already has the refresh flags
|
||||||
// set below so it will have to update anyway.
|
// set below so it will have to update anyway.
|
||||||
@ -1539,6 +1572,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
|
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
|
||||||
capsule.write(localTransform, "transform", Transform.IDENTITY);
|
capsule.write(localTransform, "transform", Transform.IDENTITY);
|
||||||
capsule.write(localLights, "lights", null);
|
capsule.write(localLights, "lights", null);
|
||||||
|
capsule.writeSavableArrayList(localOverrides, "overrides", null);
|
||||||
|
|
||||||
// Shallow clone the controls array to convert its type.
|
// Shallow clone the controls array to convert its type.
|
||||||
capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
|
capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null);
|
||||||
@ -1562,6 +1596,11 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
|
|||||||
localLights = (LightList) ic.readSavable("lights", null);
|
localLights = (LightList) ic.readSavable("lights", null);
|
||||||
localLights.setOwner(this);
|
localLights.setOwner(this);
|
||||||
|
|
||||||
|
localOverrides = ic.readSavableArrayList("overrides", null);
|
||||||
|
if (localOverrides == null) {
|
||||||
|
localOverrides = new ArrayList<MatParamOverride>();
|
||||||
|
}
|
||||||
|
worldOverrides = new ArrayList<MatParamOverride>();
|
||||||
//changed for backward compatibility with j3o files generated before the AnimControl/SkeletonControl split
|
//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 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.
|
//The SkeletonControl must be the last in the stack so we add the list of all other control before it.
|
||||||
|
174
jme3-core/src/test/java/com/jme3/scene/MPOTestUtils.java
Normal file
174
jme3-core/src/test/java/com/jme3/scene/MPOTestUtils.java
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.scene;
|
||||||
|
|
||||||
|
import com.jme3.material.MatParamOverride;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.shader.VarType;
|
||||||
|
import static com.jme3.shader.VarType.Texture2D;
|
||||||
|
import com.jme3.texture.Texture2D;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class MPOTestUtils {
|
||||||
|
|
||||||
|
private static final Camera DUMMY_CAM = new Camera(640, 480);
|
||||||
|
|
||||||
|
private static final SceneGraphVisitor VISITOR = new SceneGraphVisitor() {
|
||||||
|
@Override
|
||||||
|
public void visit(Spatial spatial) {
|
||||||
|
validateSubScene(spatial);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static void validateSubScene(Spatial scene) {
|
||||||
|
scene.checkCulling(DUMMY_CAM);
|
||||||
|
|
||||||
|
Set<MatParamOverride> actualOverrides = new HashSet<MatParamOverride>();
|
||||||
|
for (MatParamOverride override : scene.getWorldOverrides()) {
|
||||||
|
actualOverrides.add(override);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<MatParamOverride> expectedOverrides = new HashSet<MatParamOverride>();
|
||||||
|
Spatial current = scene;
|
||||||
|
while (current != null) {
|
||||||
|
for (MatParamOverride override : current.getLocalOverrides()) {
|
||||||
|
expectedOverrides.add(override);
|
||||||
|
}
|
||||||
|
current = current.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("For " + scene, expectedOverrides, actualOverrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateScene(Spatial scene) {
|
||||||
|
scene.updateGeometricState();
|
||||||
|
scene.depthFirstTraversal(VISITOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatParamOverride mpoInt(String name, int value) {
|
||||||
|
return new MatParamOverride(VarType.Int, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatParamOverride mpoBool(String name, boolean value) {
|
||||||
|
return new MatParamOverride(VarType.Boolean, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatParamOverride mpoFloat(String name, float value) {
|
||||||
|
return new MatParamOverride(VarType.Float, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatParamOverride mpoMatrix4Array(String name, Matrix4f[] value) {
|
||||||
|
return new MatParamOverride(VarType.Matrix4Array, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatParamOverride mpoTexture2D(String name, Texture2D texture) {
|
||||||
|
return new MatParamOverride(VarType.Texture2D, name, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getRefreshFlags(Spatial scene) {
|
||||||
|
try {
|
||||||
|
Field refreshFlagsField = Spatial.class.getDeclaredField("refreshFlags");
|
||||||
|
refreshFlagsField.setAccessible(true);
|
||||||
|
return (Integer) refreshFlagsField.get(scene);
|
||||||
|
} catch (NoSuchFieldException ex) {
|
||||||
|
throw new AssertionError(ex);
|
||||||
|
} catch (SecurityException ex) {
|
||||||
|
throw new AssertionError(ex);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
throw new AssertionError(ex);
|
||||||
|
} catch (IllegalAccessException ex) {
|
||||||
|
throw new AssertionError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dumpSceneRF(Spatial scene, String indent, boolean last, int refreshFlagsMask) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append(indent);
|
||||||
|
if (last) {
|
||||||
|
if (!indent.isEmpty()) {
|
||||||
|
sb.append("└─");
|
||||||
|
} else {
|
||||||
|
sb.append(" ");
|
||||||
|
}
|
||||||
|
indent += " ";
|
||||||
|
} else {
|
||||||
|
sb.append("├─");
|
||||||
|
indent += "│ ";
|
||||||
|
}
|
||||||
|
sb.append(scene.getName());
|
||||||
|
int rf = getRefreshFlags(scene) & refreshFlagsMask;
|
||||||
|
if (rf != 0) {
|
||||||
|
sb.append("(");
|
||||||
|
if ((rf & 0x1) != 0) {
|
||||||
|
sb.append("T");
|
||||||
|
}
|
||||||
|
if ((rf & 0x2) != 0) {
|
||||||
|
sb.append("B");
|
||||||
|
}
|
||||||
|
if ((rf & 0x4) != 0) {
|
||||||
|
sb.append("L");
|
||||||
|
}
|
||||||
|
if ((rf & 0x8) != 0) {
|
||||||
|
sb.append("l");
|
||||||
|
}
|
||||||
|
if ((rf & 0x10) != 0) {
|
||||||
|
sb.append("O");
|
||||||
|
}
|
||||||
|
sb.append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scene.getLocalOverrides().isEmpty()) {
|
||||||
|
sb.append(" [MPO]");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(sb);
|
||||||
|
|
||||||
|
if (scene instanceof Node) {
|
||||||
|
Node node = (Node) scene;
|
||||||
|
int childIndex = 0;
|
||||||
|
for (Spatial child : node.getChildren()) {
|
||||||
|
boolean childLast = childIndex == node.getQuantity() - 1;
|
||||||
|
dumpSceneRF(child, indent, childLast, refreshFlagsMask);
|
||||||
|
childIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dumpSceneRF(Spatial scene, int refreshFlagsMask) {
|
||||||
|
dumpSceneRF(scene, "", true, refreshFlagsMask);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.scene;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager;
|
||||||
|
import com.jme3.export.binary.BinaryExporter;
|
||||||
|
import com.jme3.material.MatParamOverride;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static com.jme3.scene.MPOTestUtils.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import com.jme3.system.TestUtil;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
|
public class SceneMatParamOverrideTest {
|
||||||
|
|
||||||
|
|
||||||
|
private static Node createDummyScene() {
|
||||||
|
Node scene = new Node("Scene Node");
|
||||||
|
|
||||||
|
Node a = new Node("A");
|
||||||
|
Node b = new Node("B");
|
||||||
|
|
||||||
|
Node c = new Node("C");
|
||||||
|
Node d = new Node("D");
|
||||||
|
|
||||||
|
Node e = new Node("E");
|
||||||
|
Node f = new Node("F");
|
||||||
|
|
||||||
|
Node g = new Node("G");
|
||||||
|
Node h = new Node("H");
|
||||||
|
Node j = new Node("J");
|
||||||
|
|
||||||
|
scene.attachChild(a);
|
||||||
|
scene.attachChild(b);
|
||||||
|
|
||||||
|
a.attachChild(c);
|
||||||
|
a.attachChild(d);
|
||||||
|
|
||||||
|
b.attachChild(e);
|
||||||
|
b.attachChild(f);
|
||||||
|
|
||||||
|
c.attachChild(g);
|
||||||
|
c.attachChild(h);
|
||||||
|
c.attachChild(j);
|
||||||
|
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrides_AddAfterAttach() {
|
||||||
|
Node scene = createDummyScene();
|
||||||
|
scene.updateGeometricState();
|
||||||
|
|
||||||
|
Node root = new Node("Root Node");
|
||||||
|
root.updateGeometricState();
|
||||||
|
|
||||||
|
root.attachChild(scene);
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||||
|
|
||||||
|
validateScene(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrides_AddBeforeAttach() {
|
||||||
|
Node scene = createDummyScene();
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||||
|
scene.updateGeometricState();
|
||||||
|
|
||||||
|
Node root = new Node("Root Node");
|
||||||
|
root.updateGeometricState();
|
||||||
|
|
||||||
|
root.attachChild(scene);
|
||||||
|
|
||||||
|
validateScene(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrides_RemoveBeforeAttach() {
|
||||||
|
Node scene = createDummyScene();
|
||||||
|
scene.updateGeometricState();
|
||||||
|
|
||||||
|
Node root = new Node("Root Node");
|
||||||
|
root.updateGeometricState();
|
||||||
|
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||||
|
validateScene(scene);
|
||||||
|
|
||||||
|
scene.getChild("A").clearMatParamOverrides();
|
||||||
|
validateScene(scene);
|
||||||
|
|
||||||
|
root.attachChild(scene);
|
||||||
|
validateScene(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrides_RemoveAfterAttach() {
|
||||||
|
Node scene = createDummyScene();
|
||||||
|
scene.updateGeometricState();
|
||||||
|
|
||||||
|
Node root = new Node("Root Node");
|
||||||
|
root.updateGeometricState();
|
||||||
|
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||||
|
|
||||||
|
root.attachChild(scene);
|
||||||
|
validateScene(root);
|
||||||
|
|
||||||
|
scene.getChild("A").clearMatParamOverrides();
|
||||||
|
validateScene(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrides_IdenticalNames() {
|
||||||
|
Node scene = createDummyScene();
|
||||||
|
|
||||||
|
scene.getChild("A").addMatParamOverride(mpoInt("val", 5));
|
||||||
|
scene.getChild("C").addMatParamOverride(mpoInt("val", 7));
|
||||||
|
|
||||||
|
validateScene(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrides_CloningScene_DoesntCloneMPO() {
|
||||||
|
Node originalScene = createDummyScene();
|
||||||
|
|
||||||
|
originalScene.getChild("A").addMatParamOverride(mpoInt("int", 5));
|
||||||
|
originalScene.getChild("A").addMatParamOverride(mpoBool("bool", true));
|
||||||
|
originalScene.getChild("A").addMatParamOverride(mpoFloat("float", 3.12f));
|
||||||
|
|
||||||
|
Node clonedScene = originalScene.clone(false);
|
||||||
|
|
||||||
|
validateScene(clonedScene);
|
||||||
|
validateScene(originalScene);
|
||||||
|
|
||||||
|
ArrayList<MatParamOverride> clonedOverrides = clonedScene.getChild("A").getLocalOverrides();
|
||||||
|
ArrayList<MatParamOverride> originalOverrides = originalScene.getChild("A").getLocalOverrides();
|
||||||
|
|
||||||
|
assertNotSame(clonedOverrides, originalOverrides);
|
||||||
|
assertEquals(clonedOverrides, originalOverrides);
|
||||||
|
|
||||||
|
for (int i = 0; i < clonedOverrides.size(); i++) {
|
||||||
|
assertSame(clonedOverrides.get(i), originalOverrides.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrides_SaveAndLoad_KeepsMPOs() {
|
||||||
|
MatParamOverride override = mpoInt("val", 5);
|
||||||
|
Node scene = createDummyScene();
|
||||||
|
scene.getChild("A").addMatParamOverride(override);
|
||||||
|
|
||||||
|
AssetManager assetManager = TestUtil.createAssetManager();
|
||||||
|
Node loadedScene = BinaryExporter.saveAndLoad(assetManager, scene);
|
||||||
|
|
||||||
|
Node root = new Node("Root Node");
|
||||||
|
root.attachChild(loadedScene);
|
||||||
|
validateScene(root);
|
||||||
|
validateScene(scene);
|
||||||
|
|
||||||
|
assertNotSame(override, loadedScene.getChild("A").getLocalOverrides().get(0));
|
||||||
|
assertEquals(override, loadedScene.getChild("A").getLocalOverrides().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEquals() {
|
||||||
|
assertEquals(mpoInt("val", 5), mpoInt("val", 5));
|
||||||
|
assertEquals(mpoBool("val", true), mpoBool("val", true));
|
||||||
|
assertNotEquals(mpoInt("val", 5), mpoInt("val", 6));
|
||||||
|
assertNotEquals(mpoInt("val1", 5), mpoInt("val2", 5));
|
||||||
|
assertNotEquals(mpoBool("val", true), mpoInt("val", 1));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user