From d3ba691600e1bd4eadae84137b5e9bc134c509ca Mon Sep 17 00:00:00 2001 From: shadowislord Date: Sat, 7 Jun 2014 18:34:37 -0400 Subject: [PATCH] * Introduce GeometryGroupNode as abstract class for implementations that group / optimize geometries * Reimplement BatchNode on top of GeometryBatchNode --- .../main/java/com/jme3/scene/BatchNode.java | 34 ++++- .../main/java/com/jme3/scene/Geometry.java | 120 ++++++++++++------ .../com/jme3/scene/GeometryGroupNode.java | 73 +++++++++++ 3 files changed, 184 insertions(+), 43 deletions(-) create mode 100644 jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java diff --git a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java index aec62c01e..8c12f3c32 100644 --- a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java @@ -34,7 +34,6 @@ package com.jme3.scene; import com.jme3.export.*; import com.jme3.material.Material; import com.jme3.math.Matrix4f; -import com.jme3.math.Transform; import com.jme3.math.Vector3f; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.util.SafeArrayList; @@ -65,7 +64,7 @@ import java.util.logging.Logger; * TODO more automagic (batch when needed in the updateLogicalState) * @author Nehon */ -public class BatchNode extends Node implements Savable { +public class BatchNode extends GeometryGroupNode implements Savable { private static final Logger logger = Logger.getLogger(BatchNode.class.getName()); /** @@ -96,6 +95,29 @@ public class BatchNode extends Node implements Savable { public BatchNode(String name) { super(name); } + + @Override + public void onTransformChange(Geometry geom) { + updateSubBatch(geom); + } + + @Override + public void onMaterialChange(Geometry geom) { + throw new UnsupportedOperationException( + "Cannot set the material of a batched geometry, " + + "change the material of the parent BatchNode."); + } + + @Override + public void onMeshChange(Geometry geom) { + throw new UnsupportedOperationException( + "Cannot set the mesh of a batched geometry"); + } + + @Override + public void onGeoemtryUnassociated(Geometry geom) { + setNeedsFullRebatch(true); + } @Override public void updateGeometricState() { @@ -284,8 +306,8 @@ public class BatchNode extends Node implements Savable { } } else if (s instanceof Geometry) { Geometry g = (Geometry) s; - if (g.isBatched()) { - g.unBatch(); + if (g.isGrouped()) { + g.unassociateFromGroupNode(); } } } @@ -297,7 +319,7 @@ public class BatchNode extends Node implements Savable { if (!isBatch(n) && n.getBatchHint() != BatchHint.Never) { Geometry g = (Geometry) n; - if (!g.isBatched() || rebatch) { + if (!g.isGrouped() || rebatch) { if (g.getMaterial() == null) { throw new IllegalStateException("No material is set for Geometry: " + g.getName() + " please set a material before batching"); } @@ -542,7 +564,7 @@ public class BatchNode extends Node implements Savable { for (Geometry geom : geometries) { Mesh inMesh = geom.getMesh(); if (!isBatch(geom)) { - geom.batch(this, globalVertIndex); + geom.associateWithGroupNode(this, globalVertIndex); } int geomVertCount = inMesh.getVertexCount(); diff --git a/jme3-core/src/main/java/com/jme3/scene/Geometry.java b/jme3-core/src/main/java/com/jme3/scene/Geometry.java index 5c5bea10d..1ce159c78 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Geometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/Geometry.java @@ -41,7 +41,7 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.material.Material; import com.jme3.math.Matrix4f; -import com.jme3.math.Transform; +import com.jme3.renderer.Camera; import com.jme3.scene.VertexBuffer.Type; import com.jme3.util.TempVars; import java.io.IOException; @@ -71,12 +71,15 @@ public class Geometry extends Spatial { */ protected boolean ignoreTransform = false; protected transient Matrix4f cachedWorldMat = new Matrix4f(); + /** - * used when geometry is batched + * Specifies which {@link GeometryGroupNode} this Geometry + * is managed by. */ - protected BatchNode batchNode = null; + protected GeometryGroupNode groupNode; /** - * the start index of this geometry's mesh in the batchNode mesh + * The start index of this Geometry's inside + * the {@link GeometryGroupNode}. */ protected int startIndex; /** @@ -106,12 +109,22 @@ public class Geometry extends Spatial { */ public Geometry(String name, Mesh mesh) { this(name); + if (mesh == null) { - throw new NullPointerException(); + throw new IllegalArgumentException("mesh cannot be null"); } this.mesh = mesh; } + + @Override + public boolean checkCulling(Camera cam) { + if (isGrouped()) { + setLastFrustumIntersection(Camera.FrustumIntersect.Outside); + return false; + } + return super.checkCulling(cam); + } /** * @return If ignoreTransform mode is set. @@ -148,6 +161,10 @@ public class Geometry extends Spatial { } lodLevel = lod; + + if (isGrouped()) { + groupNode.onMeshChange(this); + } } /** @@ -192,12 +209,13 @@ public class Geometry extends Spatial { if (mesh == null) { throw new IllegalArgumentException(); } - if (isBatched()) { - throw new UnsupportedOperationException("Cannot set the mesh of a batched geometry"); - } this.mesh = mesh; setBoundRefresh(); + + if (isGrouped()) { + groupNode.onMeshChange(this); + } } /** @@ -218,10 +236,11 @@ public class Geometry extends Spatial { */ @Override public void setMaterial(Material material) { - if (isBatched()) { - throw new UnsupportedOperationException("Cannot set the material of a batched geometry, change the material of the parent BatchNode."); - } this.material = material; + + if (isGrouped()) { + groupNode.onMaterialChange(this); + } } /** @@ -278,39 +297,48 @@ public class Geometry extends Spatial { @Override protected void updateWorldTransforms() { - super.updateWorldTransforms(); computeWorldMatrix(); - if (isBatched()) { - batchNode.updateSubBatch(this); + if (isGrouped()) { + groupNode.onTransformChange(this); } + // geometry requires lights to be sorted worldLights.sort(true); } /** - * Batch this geometry, should only be called by the BatchNode. - * @param node the batchNode - * @param startIndex the starting index of this geometry in the batched mesh + * Associate this Geometry with a {@link GeometryGroupNode}. + * + * Should only be called by the parent {@link GeometryGroupNode}. + * + * @param node Which {@link GeometryGroupNode} to associate with. + * @param startIndex The starting index of this geometry in the group. */ - protected void batch(BatchNode node, int startIndex) { - this.batchNode = node; - this.startIndex = startIndex; - setCullHint(CullHint.Always); + protected void associateWithGroupNode(GeometryGroupNode node, int startIndex) { + if (isGrouped()) { + unassociateFromGroupNode(); + } + + this.groupNode = node; + this.startIndex = startIndex; } /** - * unBatch this geometry. + * Removes the {@link GeometryGroupNode} association from this + * Geometry. + * + * Should only be called by the parent {@link GeometryGroupNode}. */ - protected void unBatch() { - this.startIndex = 0; - //once the geometry is removed from the screnegraph the batchNode needs to be rebatched. - if (batchNode != null) { - this.batchNode.setNeedsFullRebatch(true); - this.batchNode = null; + protected void unassociateFromGroupNode() { + if (groupNode != null) { + // Once the geometry is removed + // from the parent, the group node needs to be updated. + groupNode.onGeoemtryUnassociated(this); + groupNode = null; + startIndex = 0; } - setCullHint(CullHint.Dynamic); } @Override @@ -321,9 +349,10 @@ public class Geometry extends Spatial { @Override protected void setParent(Node parent) { super.setParent(parent); - //if the geometry is batched we also have to unbatch it - if (parent == null && isBatched()) { - unBatch(); + + // If the geometry is managed by group node we need to unassociate. + if (parent == null && isGrouped()) { + unassociateFromGroupNode(); } } @@ -424,8 +453,22 @@ public class Geometry extends Spatial { protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) { } + /** + * Determine whether this Geometry is managed by a + * {@link GeometryGroupNode} or not. + * + * @return True if managed by a {@link GeometryGroupNode}. + */ + public boolean isGrouped() { + return groupNode != null; + } + + /** + * @deprecated Use {@link #isGrouped()} instead. + */ + @Deprecated public boolean isBatched() { - return batchNode != null; + return isGrouped(); } /** @@ -438,11 +481,14 @@ public class Geometry extends Spatial { @Override public Geometry clone(boolean cloneMaterial) { Geometry geomClone = (Geometry) super.clone(cloneMaterial); - //this geometry is batched but the clonned one should not be - if (isBatched()) { - geomClone.batchNode = null; - geomClone.unBatch(); + + // This geometry is managed, + // but the cloned one is not attached to anything, hence not managed. + if (isGrouped()) { + groupNode = null; + startIndex = 0; } + geomClone.cachedWorldMat = cachedWorldMat.clone(); if (material != null) { if (cloneMaterial) { diff --git a/jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java b/jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java new file mode 100644 index 000000000..52b299d56 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java @@ -0,0 +1,73 @@ +package com.jme3.scene; + +/** + * An abstract class for implementations that perform grouping of geometries + * via instancing or batching. + * + * @author Kirill Vainer + */ +public abstract class GeometryGroupNode extends Node { + + /** + * Construct a GeometryGroupNode + */ + public GeometryGroupNode() { + super(); + } + + /** + * Construct a GeometryGroupNode + * + * @param name The name of the GeometryGroupNode. + */ + public GeometryGroupNode(String name) { + super(name); + } + + /** + * Called by {@link Geometry geom} to specify that its world transform + * has been changed. + * + * @param geom The Geometry whose transform changed. + */ + public abstract void onTransformChange(Geometry geom); + + /** + * Called by {@link Geometry geom} to specify that its + * {@link Geometry#setMaterial(com.jme3.material.Material) material} + * has been changed. + * + * @param geom The Geometry whose material changed. + * + * @throws UnsupportedOperationException If this implementation does + * not support dynamic material changes. + */ + public abstract void onMaterialChange(Geometry geom); + + /** + * Called by {@link Geometry geom} to specify that its + * {@link Geometry#setMesh(com.jme3.scene.Mesh) mesh} + * has been changed. + * + * This is also called when the geometry's + * {@link Geometry#setLodLevel(int) lod level} changes. + * + * @param geom The Geometry whose mesh changed. + * + * @throws UnsupportedOperationException If this implementation does + * not support dynamic mesh changes. + */ + public abstract void onMeshChange(Geometry geom); + + /** + * Called by {@link Geometry geom} to specify that it + * has been unassociated from its GeoemtryGroupNode. + * + * Unassociation occurs when the {@link Geometry} is + * {@link Spatial#removeFromParent() detached} from its parent + * {@link Node}. + * + * @param geom The Geometry which is being unassociated. + */ + public abstract void onGeoemtryUnassociated(Geometry geom); +}