* Fix outstanding cloning related issues in InstancedNode

* Throw exception if the geometry's material does not support instancing
 * Test the cloning function in TestInstanceNode
experimental
shadowislord 11 years ago
parent 6ddc68278b
commit fa41da59a4
  1. 116
      jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java
  2. 26
      jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java

@ -9,8 +9,11 @@ import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.UserData;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.material.MatParam;
import java.io.IOException;
import java.util.HashMap;
public class InstancedNode extends GeometryGroupNode {
@ -72,7 +75,7 @@ public class InstancedNode extends GeometryGroupNode {
}
}
private static class InstancedNodeControl extends AbstractControl {
private static class InstancedNodeControl implements Control {
private InstancedNode node;
@ -90,22 +93,31 @@ public class InstancedNode extends GeometryGroupNode {
// fixed automatically by InstancedNode.clone() method.
}
@Override
protected void controlUpdate(float tpf) {
public void setSpatial(Spatial spatial){
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
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 final HashMap<Geometry, InstancedGeometry> igByGeom
protected InstancedNodeControl control;
protected HashMap<Geometry, InstancedGeometry> igByGeom
= new HashMap<Geometry, InstancedGeometry>();
private final InstanceTypeKey lookUp = new InstanceTypeKey();
private InstanceTypeKey lookUp = new InstanceTypeKey();
private final HashMap<InstanceTypeKey, InstancedGeometry> instancesMap =
private HashMap<InstanceTypeKey, InstancedGeometry> instancesMap =
new HashMap<InstanceTypeKey, InstancedGeometry>();
public InstancedNode() {
@ -116,7 +128,8 @@ public class InstancedNode extends GeometryGroupNode {
public InstancedNode(String name) {
super(name);
addControl(new InstancedNodeControl(this));
control = new InstancedNodeControl(this);
addControl(control);
}
private void renderFromControl() {
@ -125,10 +138,6 @@ public class InstancedNode extends GeometryGroupNode {
}
}
private static boolean isInstancedGeometry(Geometry geom) {
return geom instanceof InstancedGeometry;
}
private InstancedGeometry lookUpByGeometry(Geometry geom) {
lookUp.mesh = geom.getMesh();
lookUp.material = geom.getMaterial();
@ -138,6 +147,7 @@ public class InstancedNode extends GeometryGroupNode {
if (ig == null) {
ig = new InstancedGeometry(
"mesh-" + System.identityHashCode(lookUp.mesh) + "," +
"material-" + lookUp.material.getMaterialDef().getName() + ","
+ "lod-" + lookUp.lodLevel);
ig.setMaterial(lookUp.material);
@ -151,6 +161,21 @@ public class InstancedNode extends GeometryGroupNode {
return ig;
}
private void addToInstancedGeometry(Geometry geom) {
Material material = geom.getMaterial();
MatParam param = material.getParam("UseInstancing");
if (param == null || !((Boolean)param.getValue()).booleanValue()) {
throw new IllegalStateException("You must set the 'UseInstancing' "
+ "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) {
@ -158,6 +183,19 @@ public class InstancedNode extends GeometryGroupNode {
}
}
private void relocateInInstancedGeometry(Geometry geom) {
InstancedGeometry oldIG = igByGeom.get(geom);
InstancedGeometry newIG = lookUpByGeometry(geom);
if (oldIG != newIG) {
if (oldIG == null) {
throw new AssertionError();
}
oldIG.deleteInstance(geom);
newIG.addInstance(geom);
igByGeom.put(geom, newIG);
}
}
private void ungroupSceneGraph(Spatial s) {
if (s instanceof Node) {
for (Spatial sp : ((Node) s).getChildren()) {
@ -168,6 +206,7 @@ public class InstancedNode extends GeometryGroupNode {
if (g.isGrouped()) {
// Will invoke onGeometryUnassociated automatically.
g.unassociateFromGroupNode();
if (InstancedNode.getGeometryStartIndex(g) != -1) {
throw new AssertionError();
}
@ -188,10 +227,7 @@ public class InstancedNode extends GeometryGroupNode {
if (n instanceof Geometry) {
Geometry g = (Geometry) n;
if (!g.isGrouped() && g.getBatchHint() != BatchHint.Never) {
InstancedGeometry ig = lookUpByGeometry(g);
igByGeom.put(g, ig);
g.associateWithGroupNode(this, 0);
ig.addInstance(g);
addToInstancedGeometry(g);
}
} else if (n instanceof Node) {
for (Spatial child : ((Node) n).getChildren()) {
@ -207,33 +243,43 @@ public class InstancedNode extends GeometryGroupNode {
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++) {
if (clone.children.get(i) instanceof InstancedGeometry) {
clone.children.remove(i);
} else if (clone.children.get(i) instanceof Geometry) {
Geometry geom = (Geometry) clone.children.get(i);
if (geom.isGrouped()) {
throw new AssertionError();
}
}
}
// Clear state (which is incorrect)
clone.igByGeom.clear();
clone.instancesMap.clear();
clone.instance();
}
return clone;
}
private void majorChange(Geometry geom) {
InstancedGeometry oldIG = igByGeom.get(geom);
InstancedGeometry newIG = lookUpByGeometry(geom);
if (oldIG != newIG) {
oldIG.deleteInstance(geom);
newIG.addInstance(geom);
igByGeom.put(geom, newIG);
}
// remove original control from the clone
clone.controls.remove(this.control);
// put clone's control in
clone.control = new InstancedNodeControl(clone);
clone.controls.add(clone.control);
clone.lookUp = new InstanceTypeKey();
clone.igByGeom = new HashMap<Geometry, InstancedGeometry>();
clone.instancesMap = new HashMap<InstanceTypeKey, InstancedGeometry>();
clone.instance();
return clone;
}
@Override
@ -243,12 +289,12 @@ public class InstancedNode extends GeometryGroupNode {
@Override
public void onMaterialChange(Geometry geom) {
majorChange(geom);
relocateInInstancedGeometry(geom);
}
@Override
public void onMeshChange(Geometry geom) {
majorChange(geom);
relocateInInstancedGeometry(geom);
}
@Override

@ -41,6 +41,7 @@ import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Spatial;
import com.jme3.scene.Node;
import com.jme3.scene.instancing.InstancedGeometry;
import com.jme3.scene.instancing.InstancedNode;
import com.jme3.scene.shape.Box;
@ -52,8 +53,9 @@ public class TestInstanceNode extends SimpleApplication {
private Mesh mesh1;
private Mesh mesh2;
private final Material[] materials = new Material[6];
private InstancedNode instancedNode;
private Node instancedNode;
private float time = 0;
private boolean INSTANCING = false;
public static void main(String[] args){
TestInstanceNode app = new TestInstanceNode();
@ -79,27 +81,27 @@ public class TestInstanceNode extends SimpleApplication {
mesh2 = new Box(0.4f, 0.4f, 0.4f);
materials[0] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
materials[0].setBoolean("UseInstancing", true);
materials[0].setBoolean("UseInstancing", INSTANCING);
materials[0].setColor("Color", ColorRGBA.Red);
materials[1] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
materials[1].setBoolean("UseInstancing", true);
materials[1].setBoolean("UseInstancing", INSTANCING);
materials[1].setColor("Color", ColorRGBA.Green);
materials[2] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
materials[2].setBoolean("UseInstancing", true);
materials[2].setBoolean("UseInstancing", INSTANCING);
materials[2].setColor("Color", ColorRGBA.Blue);
materials[3] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
materials[3].setBoolean("UseInstancing", true);
materials[3].setBoolean("UseInstancing", INSTANCING);
materials[3].setColor("Color", ColorRGBA.Cyan);
materials[4] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
materials[4].setBoolean("UseInstancing", true);
materials[4].setBoolean("UseInstancing", INSTANCING);
materials[4].setColor("Color", ColorRGBA.Magenta);
materials[5] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
materials[5].setBoolean("UseInstancing", true);
materials[5].setBoolean("UseInstancing", INSTANCING);
materials[5].setColor("Color", ColorRGBA.Yellow);
instancedNode = new InstancedNode("instanced_node");
@ -120,12 +122,18 @@ public class TestInstanceNode extends SimpleApplication {
}
}
instancedNode.instance();
if (INSTANCING) {
((InstancedNode)instancedNode).instance();
}
instancedNode = (InstancedNode) instancedNode.clone();
instancedNode.move(0, 5, 0);
rootNode.attachChild(instancedNode);
cam.setLocation(new Vector3f(38.373516f, 6.689055f, 38.482082f));
cam.setRotation(new Quaternion(-0.04004206f, 0.918326f, -0.096310444f, -0.38183528f));
flyCam.setMoveSpeed(15);
//flyCam.setEnabled(false);
flyCam.setEnabled(false);
}
private float smoothstep(float edge0, float edge1, float x) {

Loading…
Cancel
Save