* Fix outstanding cloning related issues in InstancedNode
* Throw exception if the geometry's material does not support instancing * Test the cloning function in TestInstanceNode
This commit is contained in:
parent
6ddc68278b
commit
fa41da59a4
@ -9,8 +9,11 @@ import com.jme3.scene.Mesh;
|
|||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.UserData;
|
import com.jme3.scene.UserData;
|
||||||
import com.jme3.scene.control.AbstractControl;
|
|
||||||
import com.jme3.scene.control.Control;
|
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;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class InstancedNode extends GeometryGroupNode {
|
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;
|
private InstancedNode node;
|
||||||
|
|
||||||
@ -90,22 +93,31 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
// fixed automatically by InstancedNode.clone() method.
|
// fixed automatically by InstancedNode.clone() method.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setSpatial(Spatial spatial){
|
||||||
protected void controlUpdate(float tpf) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void update(float tpf){
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
}
|
||||||
|
|
||||||
|
public void render(RenderManager rm, ViewPort vp) {
|
||||||
node.renderFromControl();
|
node.renderFromControl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final HashMap<Geometry, InstancedGeometry> igByGeom
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InstancedNodeControl control;
|
||||||
|
|
||||||
|
protected HashMap<Geometry, InstancedGeometry> igByGeom
|
||||||
= new HashMap<Geometry, InstancedGeometry>();
|
= 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>();
|
new HashMap<InstanceTypeKey, InstancedGeometry>();
|
||||||
|
|
||||||
public InstancedNode() {
|
public InstancedNode() {
|
||||||
@ -116,7 +128,8 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
|
|
||||||
public InstancedNode(String name) {
|
public InstancedNode(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
addControl(new InstancedNodeControl(this));
|
control = new InstancedNodeControl(this);
|
||||||
|
addControl(control);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderFromControl() {
|
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) {
|
private InstancedGeometry lookUpByGeometry(Geometry geom) {
|
||||||
lookUp.mesh = geom.getMesh();
|
lookUp.mesh = geom.getMesh();
|
||||||
lookUp.material = geom.getMaterial();
|
lookUp.material = geom.getMaterial();
|
||||||
@ -138,6 +147,7 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
|
|
||||||
if (ig == null) {
|
if (ig == null) {
|
||||||
ig = new InstancedGeometry(
|
ig = new InstancedGeometry(
|
||||||
|
"mesh-" + System.identityHashCode(lookUp.mesh) + "," +
|
||||||
"material-" + lookUp.material.getMaterialDef().getName() + ","
|
"material-" + lookUp.material.getMaterialDef().getName() + ","
|
||||||
+ "lod-" + lookUp.lodLevel);
|
+ "lod-" + lookUp.lodLevel);
|
||||||
ig.setMaterial(lookUp.material);
|
ig.setMaterial(lookUp.material);
|
||||||
@ -151,6 +161,21 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
return ig;
|
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) {
|
private void removeFromInstancedGeometry(Geometry geom) {
|
||||||
InstancedGeometry ig = igByGeom.remove(geom);
|
InstancedGeometry ig = igByGeom.remove(geom);
|
||||||
if (ig != null) {
|
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) {
|
private void ungroupSceneGraph(Spatial s) {
|
||||||
if (s instanceof Node) {
|
if (s instanceof Node) {
|
||||||
for (Spatial sp : ((Node) s).getChildren()) {
|
for (Spatial sp : ((Node) s).getChildren()) {
|
||||||
@ -168,6 +206,7 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
if (g.isGrouped()) {
|
if (g.isGrouped()) {
|
||||||
// Will invoke onGeometryUnassociated automatically.
|
// Will invoke onGeometryUnassociated automatically.
|
||||||
g.unassociateFromGroupNode();
|
g.unassociateFromGroupNode();
|
||||||
|
|
||||||
if (InstancedNode.getGeometryStartIndex(g) != -1) {
|
if (InstancedNode.getGeometryStartIndex(g) != -1) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
@ -188,10 +227,7 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
if (n instanceof Geometry) {
|
if (n instanceof Geometry) {
|
||||||
Geometry g = (Geometry) n;
|
Geometry g = (Geometry) n;
|
||||||
if (!g.isGrouped() && g.getBatchHint() != BatchHint.Never) {
|
if (!g.isGrouped() && g.getBatchHint() != BatchHint.Never) {
|
||||||
InstancedGeometry ig = lookUpByGeometry(g);
|
addToInstancedGeometry(g);
|
||||||
igByGeom.put(g, ig);
|
|
||||||
g.associateWithGroupNode(this, 0);
|
|
||||||
ig.addInstance(g);
|
|
||||||
}
|
}
|
||||||
} else if (n instanceof Node) {
|
} else if (n instanceof Node) {
|
||||||
for (Spatial child : ((Node) n).getChildren()) {
|
for (Spatial child : ((Node) n).getChildren()) {
|
||||||
@ -207,33 +243,43 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
instance(this);
|
instance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node clone() {
|
||||||
|
return clone(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node clone(boolean cloneMaterials) {
|
public Node clone(boolean cloneMaterials) {
|
||||||
InstancedNode clone = (InstancedNode)super.clone(cloneMaterials);
|
InstancedNode clone = (InstancedNode)super.clone(cloneMaterials);
|
||||||
|
|
||||||
if (instancesMap.size() > 0) {
|
if (instancesMap.size() > 0) {
|
||||||
// Remove all instanced geometries from the clone
|
// Remove all instanced geometries from the clone
|
||||||
for (int i = 0; i < clone.children.size(); i++) {
|
for (int i = 0; i < clone.children.size(); i++) {
|
||||||
if (clone.children.get(i) instanceof InstancedGeometry) {
|
if (clone.children.get(i) instanceof InstancedGeometry) {
|
||||||
clone.children.remove(i);
|
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)
|
// remove original control from the clone
|
||||||
clone.igByGeom.clear();
|
clone.controls.remove(this.control);
|
||||||
clone.instancesMap.clear();
|
|
||||||
|
// 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();
|
clone.instance();
|
||||||
}
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void majorChange(Geometry geom) {
|
return clone;
|
||||||
InstancedGeometry oldIG = igByGeom.get(geom);
|
|
||||||
InstancedGeometry newIG = lookUpByGeometry(geom);
|
|
||||||
if (oldIG != newIG) {
|
|
||||||
oldIG.deleteInstance(geom);
|
|
||||||
newIG.addInstance(geom);
|
|
||||||
igByGeom.put(geom, newIG);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -243,12 +289,12 @@ public class InstancedNode extends GeometryGroupNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMaterialChange(Geometry geom) {
|
public void onMaterialChange(Geometry geom) {
|
||||||
majorChange(geom);
|
relocateInInstancedGeometry(geom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMeshChange(Geometry geom) {
|
public void onMeshChange(Geometry geom) {
|
||||||
majorChange(geom);
|
relocateInInstancedGeometry(geom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -41,6 +41,7 @@ import com.jme3.math.Vector3f;
|
|||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.instancing.InstancedGeometry;
|
import com.jme3.scene.instancing.InstancedGeometry;
|
||||||
import com.jme3.scene.instancing.InstancedNode;
|
import com.jme3.scene.instancing.InstancedNode;
|
||||||
import com.jme3.scene.shape.Box;
|
import com.jme3.scene.shape.Box;
|
||||||
@ -52,8 +53,9 @@ public class TestInstanceNode extends SimpleApplication {
|
|||||||
private Mesh mesh1;
|
private Mesh mesh1;
|
||||||
private Mesh mesh2;
|
private Mesh mesh2;
|
||||||
private final Material[] materials = new Material[6];
|
private final Material[] materials = new Material[6];
|
||||||
private InstancedNode instancedNode;
|
private Node instancedNode;
|
||||||
private float time = 0;
|
private float time = 0;
|
||||||
|
private boolean INSTANCING = false;
|
||||||
|
|
||||||
public static void main(String[] args){
|
public static void main(String[] args){
|
||||||
TestInstanceNode app = new TestInstanceNode();
|
TestInstanceNode app = new TestInstanceNode();
|
||||||
@ -79,27 +81,27 @@ public class TestInstanceNode extends SimpleApplication {
|
|||||||
mesh2 = new Box(0.4f, 0.4f, 0.4f);
|
mesh2 = new Box(0.4f, 0.4f, 0.4f);
|
||||||
|
|
||||||
materials[0] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
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[0].setColor("Color", ColorRGBA.Red);
|
||||||
|
|
||||||
materials[1] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
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[1].setColor("Color", ColorRGBA.Green);
|
||||||
|
|
||||||
materials[2] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
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[2].setColor("Color", ColorRGBA.Blue);
|
||||||
|
|
||||||
materials[3] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
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[3].setColor("Color", ColorRGBA.Cyan);
|
||||||
|
|
||||||
materials[4] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
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[4].setColor("Color", ColorRGBA.Magenta);
|
||||||
|
|
||||||
materials[5] = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
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);
|
materials[5].setColor("Color", ColorRGBA.Yellow);
|
||||||
|
|
||||||
instancedNode = new InstancedNode("instanced_node");
|
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.setLocation(new Vector3f(38.373516f, 6.689055f, 38.482082f));
|
||||||
cam.setRotation(new Quaternion(-0.04004206f, 0.918326f, -0.096310444f, -0.38183528f));
|
cam.setRotation(new Quaternion(-0.04004206f, 0.918326f, -0.096310444f, -0.38183528f));
|
||||||
flyCam.setMoveSpeed(15);
|
flyCam.setMoveSpeed(15);
|
||||||
//flyCam.setEnabled(false);
|
flyCam.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float smoothstep(float edge0, float edge1, float x) {
|
private float smoothstep(float edge0, float edge1, float x) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user