From fa41da59a4a43d99294cb35c1c0a95aa0c862bbf Mon Sep 17 00:00:00 2001 From: shadowislord Date: Sat, 28 Jun 2014 20:52:30 -0400 Subject: [PATCH] * Fix outstanding cloning related issues in InstancedNode * Throw exception if the geometry's material does not support instancing * Test the cloning function in TestInstanceNode --- .../jme3/scene/instancing/InstancedNode.java | 120 ++++++++++++------ .../scene/instancing/TestInstanceNode.java | 26 ++-- 2 files changed, 100 insertions(+), 46 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java index 5e5c595ef..61222cc2f 100644 --- a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedNode.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 igByGeom + protected InstancedNodeControl control; + + protected HashMap igByGeom = new HashMap(); - private final InstanceTypeKey lookUp = new InstanceTypeKey(); + private InstanceTypeKey lookUp = new InstanceTypeKey(); - private final HashMap instancesMap = + private HashMap instancesMap = new HashMap(); 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() { @@ -124,11 +137,7 @@ public class InstancedNode extends GeometryGroupNode { ig.updateInstances(); } } - - 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,35 +243,45 @@ 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(); } + + // 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(); + clone.instancesMap = new HashMap(); + + 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); - } - } - @Override public void onTransformChange(Geometry geom) { // Handled automatically @@ -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 diff --git a/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java b/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java index 3efcb75d3..41e711c7f 100644 --- a/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java +++ b/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java @@ -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) {