浏览代码

* Introduce GeometryGroupNode as abstract class for implementations that group / optimize geometries
* Reimplement BatchNode on top of GeometryBatchNode

shadowislord 11 年之前
父节点
当前提交
d3ba691600

+ 28 - 6
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();

+ 83 - 37
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 <code>Geometry</code>
+     * 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 <code>Geometry's</code> 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 <code>Geometry</code> 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 
+     * <code>Geometry</code>.
+     * 
+     * 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<Spatial> queue) {
     }
 
+    /**
+     * Determine whether this <code>Geometry</code> 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) {

+ 73 - 0
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 <code>GeometryGroupNode</code>
+     */
+    public GeometryGroupNode() {
+        super();
+    }
+
+    /**
+     * Construct a <code>GeometryGroupNode</code>
+     * 
+     * @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 <code>GeoemtryGroupNode</code>.
+     * 
+     * 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);
+}