From 7193c0b2d3b399712fa2c46523daf8a40869cd8e Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Tue, 25 Oct 2011 16:46:25 +0000 Subject: [PATCH] Batching : - BatchNode can now batch a scene graph with several materials. It creates a batch by material - Added a SimpleBatchNode that batch only geometries (no sub node graph) for better performances - removed RF_REFRESHBATCH from Spatial as it's no longer needed - changed test cases a bit git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8527 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- engine/src/core/com/jme3/scene/BatchNode.java | 309 +++++++++--------- engine/src/core/com/jme3/scene/Geometry.java | 31 +- .../core/com/jme3/scene/SimpleBatchNode.java | 56 ++++ engine/src/core/com/jme3/scene/Spatial.java | 3 +- .../test/jme3test/batching/TestBatchNode.java | 9 +- .../batching/TestBatchNodeCluster.java | 103 +++--- .../jme3test/batching/TestBatchNodeTower.java | 11 +- 7 files changed, 288 insertions(+), 234 deletions(-) create mode 100644 engine/src/core/com/jme3/scene/SimpleBatchNode.java diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java index fbee295f7..22ecf1ee9 100644 --- a/engine/src/core/com/jme3/scene/BatchNode.java +++ b/engine/src/core/com/jme3/scene/BatchNode.java @@ -31,8 +31,6 @@ */ package com.jme3.scene; -import com.jme3.asset.AssetNotFoundException; -import com.jme3.bounding.BoundingVolume; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; @@ -40,31 +38,33 @@ import com.jme3.export.OutputCapsule; import com.jme3.export.Savable; 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.IntMap.Entry; -import com.jme3.util.SafeArrayList; import com.jme3.util.TempVars; import java.io.IOException; import java.nio.Buffer; import java.nio.FloatBuffer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** - * BatchNode holds a geometry that is a batched version of all geometries that are in its sub scenegraph. - * this geometry is directly attached to the node in the scene graph. - * usage is like any other node except you have to call the {@link #batch()} method once all geoms have been attached to the sub scene graph (see todo more automagic for further enhancements) + * BatchNode holds geometrie that are batched version of all geometries that are in its sub scenegraph. + * There is one geometry per different material in the sub tree. + * this geometries are directly attached to the node in the scene graph. + * usage is like any other node except you have to call the {@link #batch()} method once all geoms have been attached to the sub scene graph and theire material set + * (see todo more automagic for further enhancements) * all the geometry that have been batched are set to {@link CullHint#Always} to not render them. * the sub geometries can be transformed as usual their transforms are used to update the mesh of the geometryBatch. * sub geoms can be removed but it may be slower than the normal spatial removing * Sub geoms can be added after the batch() method has been called but won't be batched and will be rendered as normal geometries. - * To integrated them in the batch you have to call the batch() method again on the batchNode. + * To integrate them in the batch you have to call the batch() method again on the batchNode. * - * TODO account for sub-BatchNodes - * TODO account for geometries that have different materials * TODO normal or tangents or both looks a bit weird * TODO more automagic (batch when needed in the updateLigicalState) * @author Nehon @@ -73,12 +73,13 @@ public class BatchNode extends Node implements Savable { private static final Logger logger = Logger.getLogger(BatchNode.class.getName()); /** - * the geometry holding the batched mesh + * the map of geometry holding the batched meshes */ - protected Geometry batch; - protected Material material; - private boolean needMeshUpdate = false; + protected Map batches = new HashMap(); + /** + * Construct a batchNode + */ public BatchNode() { super(); } @@ -109,12 +110,17 @@ public class BatchNode extends Node implements Savable { for (Spatial child : children.getArray()) { child.updateGeometricState(); } - if (needMeshUpdate) { - batch.getMesh().updateBound(); - batch.updateWorldBound(); - needMeshUpdate = false; + + for (Batch batch : batches.values()) { + if (batch.needMeshUpdate) { + batch.geometry.getMesh().updateBound(); + batch.geometry.updateWorldBound(); + batch.needMeshUpdate = false; + + } } + } if ((refreshFlags & RF_BOUND) != 0) { @@ -123,10 +129,15 @@ public class BatchNode extends Node implements Savable { assert refreshFlags == 0; } + + protected Transform getTransforms(Geometry geom){ + return geom.getWorldTransform(); + } protected void updateSubBatch(Geometry bg) { + Batch batch = batches.get(bg.getMaterial()); if (batch != null) { - Mesh mesh = batch.getMesh(); + Mesh mesh = batch.geometry.getMesh(); FloatBuffer buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Position).getData(); doTransformVerts(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); @@ -144,162 +155,155 @@ public class BatchNode extends Node implements Savable { mesh.getBuffer(VertexBuffer.Type.Tangent).updateData(buf); } - needMeshUpdate = true; + batch.needMeshUpdate = true; } } - @Override - protected void setTransformRefresh() { - refreshFlags |= RF_TRANSFORM; - setBoundRefresh(); - - for (Spatial child : children.getArray()) { - if ((child.refreshFlags & RF_TRANSFORM) != 0) { - continue; - } - - innerTransformRefresh(child); - //child.setTransformRefresh(); - - } - } -// - private void innerTransformRefresh(Spatial s) { - s.refreshFlags |= RF_TRANSFORM; - s.setBoundRefresh(); - if (s instanceof Node) { - Node n = (Node) s; - - for (Spatial child :((SafeArrayList) n.getChildren()).getArray()) { - if ((child.refreshFlags & RF_TRANSFORM) != 0) { - continue; - } - innerTransformRefresh(child); - } - } - - } - /** * Batch this batchNode * every geometry of the sub scene graph of this node will be batched into a single mesh that will be rendered in one call */ public void batch() { + doBatch(); + //we set the batch geometries to ignore transforms to avoid transforms of parent nodes to be applied twice + for (Batch batch : batches.values()) { + batch.geometry.setIgnoreTransform(true); + } + } - List tmpList = new ArrayList(); - Mesh m = new Mesh(); - populateList(tmpList, this); - mergeGeometries(m, tmpList); - - if (batch == null) { - batch = new Geometry(name + "-batch"); - batch.setMaterial(material); - this.attachChild(batch); + protected void doBatch() { + ///List tmpList = new ArrayList(); + Map> matMap = new HashMap>(); + + gatherGeomerties(matMap, this); + batches.clear(); + int nbGeoms = 0; + for (Material material : matMap.keySet()) { + Mesh m = new Mesh(); + List list = matMap.get(material); + nbGeoms += list.size(); + mergeGeometries(m, list); + Batch batch = new Batch(); + + batch.geometry = new Geometry(name + "-batch" + batches.size()); + batch.geometry.setMaterial(material); + this.attachChild(batch.geometry); + + + batch.geometry.setMesh(m); + batch.geometry.getMesh().updateCounts(); + batch.geometry.getMesh().updateBound(); + batches.put(material, batch); } - batch.setMesh(m); - batch.getMesh().updateCounts(); - batch.getMesh().updateBound(); + logger.log(Level.INFO, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()}); } - private void populateList(List list, Spatial n) { + private void gatherGeomerties(Map> map, Spatial n) { - if (n instanceof Geometry) { - if (n != batch) { - list.add((Geometry) n); + if (n.getClass() == Geometry.class) { + if (!isBatch(n)) { + Geometry g = (Geometry) n; + if (g.getMaterial() == null) { + throw new IllegalStateException("No material is set for Geometry: " + g.getName() + " please set a material before batching"); + } + List list = map.get(g.getMaterial()); + if (list == null) { + list = new ArrayList(); + map.put(g.getMaterial(), list); + } + list.add(g); } } else if (n instanceof Node) { for (Spatial child : ((Node) n).getChildren()) { - populateList(list, child); + if (child instanceof BatchNode) { + continue; + } + gatherGeomerties(map, child); } } } + private boolean isBatch(Spatial s) { + for (Batch batch : batches.values()) { + if (batch.geometry == s) { + return true; + } + } + return false; + } + /** - * Sets the material to use for this geometry. + * Sets the material to the all the batches of this BatchNode + * use setMaterial(Material material,int batchIndex) to set a material to a specific batch * * @param material the material to use for this geometry */ @Override public void setMaterial(Material material) { - super.setMaterial(material); - if (batch != null) { - batch.setMaterial(material); - } - this.material = material; - +// for (Batch batch : batches.values()) { +// batch.geometry.setMaterial(material); +// } + throw new UnsupportedOperationException("Unsupported for now, please set the material on the geoms before batching"); } /** - * Returns the material that is used for this geometry. + * Returns the material that is used for the first batch of this BatchNode + * + * use getMaterial(Material material,int batchIndex) to get a material from a specific batch * - * @return the material that is used for this geometry + * @return the material that is used for the first batch of this BatchNode * * @see #setMaterial(com.jme3.material.Material) */ public Material getMaterial() { - return material; - } - - /** - * @return The bounding volume of the mesh, in model space. - */ - public BoundingVolume getModelBound() { - if (batch != null) { - return batch.getMesh().getBound(); + if (!batches.isEmpty()) { + Batch b = batches.get(batches.keySet().iterator().next()); + return b.geometry.getMaterial(); } - return super.getWorldBound(); - - } - - /** - * This version of clone is a shallow clone, in other words, the - * same mesh is referenced as the original geometry. - * Exception: if the mesh is marked as being a software - * animated mesh, (bind pose is set) then the positions - * and normals are deep copied. - */ - @Override - public BatchNode clone(boolean cloneMaterial) { - BatchNode clone = (BatchNode) super.clone(cloneMaterial); - clone.batch = batch.clone(cloneMaterial); - return clone; - } - - /** - * This version of clone is a shallow clone, in other words, the - * same mesh is referenced as the original geometry. - * Exception: if the mesh is marked as being a software - * animated mesh, (bind pose is set) then the positions - * and normals are deep copied. - */ - @Override - public BatchNode clone() { - return clone(true); - } - - /** - * Creates a deep clone of the geometry, - * this creates an identical copy of the mesh - * with the vertexbuffer data duplicated. - */ - @Override - public Spatial deepClone() { - BatchNode clone = clone(true); - clone.batch = (Geometry) batch.deepClone(); - return clone; + return null;//material; } + +// /** +// * Sets the material to the a specific batch of this BatchNode +// * +// * +// * @param material the material to use for this geometry +// */ +// public void setMaterial(Material material,int batchIndex) { +// if (!batches.isEmpty()) { +// +// } +// +// } +// +// /** +// * Returns the material that is used for the first batch of this BatchNode +// * +// * use getMaterial(Material material,int batchIndex) to get a material from a specific batch +// * +// * @return the material that is used for the first batch of this BatchNode +// * +// * @see #setMaterial(com.jme3.material.Material) +// */ +// public Material getMaterial(int batchIndex) { +// if (!batches.isEmpty()) { +// Batch b = batches.get(batches.keySet().iterator().next()); +// return b.geometry.getMaterial(); +// } +// return null;//material; +// } @Override public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule oc = ex.getCapsule(this); - - if (material != null) { - oc.write(material.getAssetName(), "materialName", null); - } - oc.write(material, "material", null); +// +// if (material != null) { +// oc.write(material.getAssetName(), "materialName", null); +// } +// oc.write(material, "material", null); } @@ -309,23 +313,23 @@ public class BatchNode extends Node implements Savable { InputCapsule ic = im.getCapsule(this); - material = null; - String matName = ic.readString("materialName", null); - if (matName != null) { - // Material name is set, - // Attempt to load material via J3M - try { - material = im.getAssetManager().loadMaterial(matName); - } catch (AssetNotFoundException ex) { - // Cannot find J3M file. - logger.log(Level.FINE, "Could not load J3M file {0} for Geometry.", - matName); - } - } - // If material is NULL, try to load it from the geometry - if (material == null) { - material = (Material) ic.readSavable("material", null); - } +// material = null; +// String matName = ic.readString("materialName", null); +// if (matName != null) { +// // Material name is set, +// // Attempt to load material via J3M +// try { +// material = im.getAssetManager().loadMaterial(matName); +// } catch (AssetNotFoundException ex) { +// // Cannot find J3M file. +// logger.log(Level.FINE, "Could not load J3M file {0} for Geometry.", +// matName); +// } +// } +// // If material is NULL, try to load it from the geometry +// if (material == null) { +// material = (Material) ic.readSavable("material", null); +// } } @@ -418,9 +422,6 @@ public class BatchNode extends Node implements Savable { for (Geometry geom : geometries) { Mesh inMesh = geom.getMesh(); - if (geom.getMaterial() != null && material == null) { - material = geom.getMaterial(); - } geom.batch(this, globalVertIndex); int geomVertCount = inMesh.getVertexCount(); @@ -532,4 +533,10 @@ public class BatchNode extends Node implements Savable { } vars.release(); } + + protected class Batch { + + Geometry geometry; + boolean needMeshUpdate = false; + } } diff --git a/engine/src/core/com/jme3/scene/Geometry.java b/engine/src/core/com/jme3/scene/Geometry.java index 2d41c850b..927571910 100644 --- a/engine/src/core/com/jme3/scene/Geometry.java +++ b/engine/src/core/com/jme3/scene/Geometry.java @@ -79,11 +79,11 @@ public class Geometry extends Spatial { /** * the previous transforms of the geometry used to compute world transforms */ - protected Transform prevLocalTransform = null; + protected Transform prevBatchTransforms = null; /** * the cached offset matrix used when the geometry is batched */ - protected Matrix4f cachedOffsetMat = null; + protected Matrix4f cachedOffsetMat = null; /** * Serialization only. Do not use. @@ -287,11 +287,12 @@ public class Geometry extends Spatial { super.updateWorldTransforms(); computeWorldMatrix(); - if ((refreshFlags & RF_REFRESHBATCH) != 0) { + + if (isBatched()) { computeOffsetTransform(); batchNode.updateSubBatch(this); - prevLocalTransform.set(localTransform); - refreshFlags &= ~RF_REFRESHBATCH; + prevBatchTransforms.set(batchNode.getTransforms(this)); + } // geometry requires lights to be sorted worldLights.sort(true); @@ -305,10 +306,9 @@ public class Geometry extends Spatial { protected void batch(BatchNode node, int startIndex) { this.batchNode = node; this.startIndex = startIndex; - prevLocalTransform = new Transform(); + prevBatchTransforms = new Transform(); cachedOffsetMat = new Matrix4f(); setCullHint(CullHint.Always); - refreshFlags |= RF_REFRESHBATCH; } /** @@ -316,7 +316,7 @@ public class Geometry extends Spatial { */ protected void unBatch() { this.startIndex = 0; - prevLocalTransform = null; + prevBatchTransforms = null; cachedOffsetMat = null; //once the geometry is removed from the screnegraph we call batch on the batchNode before unreferencing it. this.batchNode.batch(); @@ -343,21 +343,21 @@ public class Geometry extends Spatial { // Compute the cached world matrix cachedOffsetMat.loadIdentity(); - cachedOffsetMat.setRotationQuaternion(prevLocalTransform.getRotation()); - cachedOffsetMat.setTranslation(prevLocalTransform.getTranslation()); + cachedOffsetMat.setRotationQuaternion(prevBatchTransforms.getRotation()); + cachedOffsetMat.setTranslation(prevBatchTransforms.getTranslation()); Matrix4f scaleMat = vars.tempMat4; scaleMat.loadIdentity(); - scaleMat.scale(prevLocalTransform.getScale()); + scaleMat.scale(prevBatchTransforms.getScale()); cachedOffsetMat.multLocal(scaleMat); cachedOffsetMat.invertLocal(); tmpMat.loadIdentity(); - tmpMat.setRotationQuaternion(localTransform.getRotation()); - tmpMat.setTranslation(localTransform.getTranslation()); + tmpMat.setRotationQuaternion(batchNode.getTransforms(this).getRotation()); + tmpMat.setTranslation(batchNode.getTransforms(this).getTranslation()); scaleMat.loadIdentity(); - scaleMat.scale(localTransform.getScale()); + scaleMat.scale(batchNode.getTransforms(this).getScale()); tmpMat.multLocal(scaleMat); tmpMat.mult(cachedOffsetMat, cachedOffsetMat); @@ -373,9 +373,6 @@ public class Geometry extends Spatial { protected void setTransformRefresh() { refreshFlags |= RF_TRANSFORM; setBoundRefresh(); - if (isBatched()) { - refreshFlags |= RF_REFRESHBATCH; - } } /** diff --git a/engine/src/core/com/jme3/scene/SimpleBatchNode.java b/engine/src/core/com/jme3/scene/SimpleBatchNode.java new file mode 100644 index 000000000..0f1ed2939 --- /dev/null +++ b/engine/src/core/com/jme3/scene/SimpleBatchNode.java @@ -0,0 +1,56 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.scene; + +import com.jme3.math.Transform; + +/** + * + * SimpleBatchNode comes with some restrictions, but can yield better performances. + * Geometries to be batched has to be attached directly to the BatchNode + * You can't attach a Node to a SimpleBatchNode + * SimpleBatchNode is recommended when you have a large number of geometries using the same material that does not require a complex scene graph structure. + * @see BatchNode + * @author Nehon + */ +public class SimpleBatchNode extends BatchNode { + + public SimpleBatchNode() { + super(); + } + + public SimpleBatchNode(String name) { + super(name); + } + + @Override + public int attachChild(Spatial child) { + + if (!(child instanceof Geometry)) { + throw new UnsupportedOperationException("BatchNode is BatchMode.Simple only support child of type Geometry, use BatchMode.Complex to use a complex structure"); + } + + return super.attachChild(child); + } + + @Override + protected void setTransformRefresh() { + + refreshFlags |= RF_TRANSFORM; + setBoundRefresh(); + for (Batch batch : batches.values()) { + batch.geometry.setTransformRefresh(); + } + } + + protected Transform getTransforms(Geometry geom){ + return geom.getLocalTransform(); + } + + @Override + public void batch() { + doBatch(); + } +} diff --git a/engine/src/core/com/jme3/scene/Spatial.java b/engine/src/core/com/jme3/scene/Spatial.java index 196faaa23..057b01b96 100644 --- a/engine/src/core/com/jme3/scene/Spatial.java +++ b/engine/src/core/com/jme3/scene/Spatial.java @@ -112,8 +112,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { */ protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms RF_BOUND = 0x02, - RF_LIGHTLIST = 0x04, // changes in light lists - RF_REFRESHBATCH = 0x08; // chamge in geometry transforms that require refreshing the batched mesh + RF_LIGHTLIST = 0x04; // changes in light lists protected CullHint cullHint = CullHint.Inherit; diff --git a/engine/src/test/jme3test/batching/TestBatchNode.java b/engine/src/test/jme3test/batching/TestBatchNode.java index dcc036813..861536cac 100644 --- a/engine/src/test/jme3test/batching/TestBatchNode.java +++ b/engine/src/test/jme3test/batching/TestBatchNode.java @@ -16,6 +16,7 @@ import com.jme3.scene.BatchNode; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.shape.Box; +import com.jme3.system.NanoTimer; import com.jme3.util.TangentBinormalGenerator; /** @@ -33,6 +34,7 @@ public class TestBatchNode extends SimpleApplication { @Override public void simpleInitApp() { + timer = new NanoTimer(); batch = new BatchNode("theBatchNode"); /** @@ -41,7 +43,8 @@ public class TestBatchNode extends SimpleApplication { */ Box boxshape4 = new Box(Vector3f.ZERO, 1f, 1f, 1f ); cube = new Geometry("cube1", boxshape4); - Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); + Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); + cube.setMaterial(mat); // Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); // mat.setColor("Diffuse", ColorRGBA.Blue); // mat.setBoolean("UseMaterialColors", true); @@ -51,7 +54,7 @@ public class TestBatchNode extends SimpleApplication { */ Box box = new Box(Vector3f.ZERO, 1f, 1f, 1f); cube2 = new Geometry("cube2", box); - + cube2.setMaterial(mat); TangentBinormalGenerator.generate(cube); TangentBinormalGenerator.generate(cube2); @@ -61,7 +64,7 @@ public class TestBatchNode extends SimpleApplication { // n.attachChild(cube2); batch.attachChild(cube); batch.attachChild(cube2); - batch.setMaterial(mat); + // batch.setMaterial(mat); batch.batch(); rootNode.attachChild(batch); cube.setLocalTranslation(3, 0, 0); diff --git a/engine/src/test/jme3test/batching/TestBatchNodeCluster.java b/engine/src/test/jme3test/batching/TestBatchNodeCluster.java index a3ebe43c2..afe806420 100644 --- a/engine/src/test/jme3test/batching/TestBatchNodeCluster.java +++ b/engine/src/test/jme3test/batching/TestBatchNodeCluster.java @@ -22,6 +22,7 @@ import com.jme3.post.FilterPostProcessor; import com.jme3.post.filters.BloomFilter; import com.jme3.scene.BatchNode; import com.jme3.scene.Node; +import com.jme3.scene.SimpleBatchNode; import com.jme3.scene.Spatial; import com.jme3.scene.debug.Arrow; import com.jme3.system.NanoTimer; @@ -60,13 +61,8 @@ public class TestBatchNodeCluster extends SimpleApplication { protected int dynamic = 4; protected static AppSettings settingst; protected boolean isTrue = true; - private int lineLength = 50; - protected BatchNode blue; - protected BatchNode brown; - protected BatchNode pink; - protected BatchNode orange; - + protected BatchNode batchNode; Material mat1; Material mat2; Material mat3; @@ -77,34 +73,33 @@ public class TestBatchNodeCluster extends SimpleApplication { @Override public void simpleInitApp() { - + timer = new NanoTimer(); + + batchNode = new SimpleBatchNode("BatchNode"); + - blue = new BatchNode("blue"); - brown = new BatchNode("brown"); - pink = new BatchNode("pink"); - orange = new BatchNode("orange"); - xPosition.add(0); yPosition.add(0); zPosition.add(0); - randomGenerator(); - + mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat1.setColor("Color", ColorRGBA.White); mat1.setColor("GlowColor", ColorRGBA.Blue.mult(10)); - blue.setMaterial(mat1); + mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat2.setColor("Color", ColorRGBA.White); mat2.setColor("GlowColor", ColorRGBA.Red.mult(10)); - brown.setMaterial(mat2); + mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat3.setColor("Color", ColorRGBA.White); mat3.setColor("GlowColor", ColorRGBA.Yellow.mult(10)); - pink.setMaterial(mat3); + mat4 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat4.setColor("Color", ColorRGBA.White); mat4.setColor("GlowColor", ColorRGBA.Orange.mult(10)); - orange.setMaterial(mat4); + + randomGenerator(); + //rootNode.attachChild(SkyFactory.createSky( // assetManager, "Textures/SKY02.zip", false)); inputManager.addMapping("Start Game", new KeyTrigger(KeyInput.KEY_J)); @@ -115,24 +110,17 @@ public class TestBatchNodeCluster extends SimpleApplication { cam.setRotation(new Quaternion(0.022630932f, 0.9749435f, -0.18736298f, 0.11776358f)); - blue.batch(); - brown.batch(); - pink.batch(); - orange.batch(); + batchNode.batch(); + terrain = new Node("terrain"); terrain.setLocalTranslation(50, 0, 50); - terrain.attachChild(blue); - terrain.attachChild(brown); - terrain.attachChild(pink); - terrain.attachChild(orange); + terrain.attachChild(batchNode); + flyCam.setMoveSpeed(100); rootNode.attachChild(terrain); Vector3f pos = new Vector3f(-40, 0, -40); - blue.setLocalTranslation(pos); - brown.setLocalTranslation(pos); - pink.setLocalTranslation(pos); - orange.setLocalTranslation(pos); + batchNode.setLocalTranslation(pos); Arrow a = new Arrow(new Vector3f(0, 50, 0)); @@ -141,7 +129,7 @@ public class TestBatchNodeCluster extends SimpleApplication { Material m = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); m.setColor("Color", ColorRGBA.Blue); g.setMaterial(m); - + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); @@ -159,35 +147,37 @@ public class TestBatchNodeCluster extends SimpleApplication { box.setLocalTranslation(new Vector3f(xPosition.get(xPosition.size() - 1), yPosition.get(yPosition.size() - 1), zPosition.get(zPosition.size() - 1))); - + batchNode.attachChild(box); if (i < 500) { - blue.attachChild(box); + box.setMaterial(mat1); } else if (i < 1000) { - brown.attachChild(box); + + box.setMaterial(mat2); } else if (i < 1500) { - pink.attachChild(box); - } else { - orange.attachChild(box); - } - } - } + box.setMaterial(mat3); + } else { - public BatchNode randomBatch() { + box.setMaterial(mat4); + } - int randomn = rand.nextInt(4); - if (randomn == 0) { - return blue; - } else if (randomn == 1) { - return brown; - } else if (randomn == 2) { - return pink; - } else if (randomn == 3) { - return orange; } - return null; } +// public BatchNode randomBatch() { +// +// int randomn = rand.nextInt(4); +// if (randomn == 0) { +// return blue; +// } else if (randomn == 1) { +// return brown; +// } else if (randomn == 2) { +// return pink; +// } else if (randomn == 3) { +// return orange; +// } +// return null; +// } public ColorRGBA randomColor() { ColorRGBA color = ColorRGBA.Black; int randomn = rand.nextInt(4); @@ -330,32 +320,27 @@ public class TestBatchNodeCluster extends SimpleApplication { @Override public void simpleUpdate(float tpf) { - time += tpf; + time += tpf; int random = rand.nextInt(2000); float mult1 = 1.0f; float mult2 = 1.0f; - BatchNode b = null; if (random < 500) { - b = blue; mult1 = 1.0f; mult2 = 1.0f; } else if (random < 1000) { - b = brown; mult1 = -1.0f; mult2 = 1.0f; } else if (random < 1500) { - b = pink; mult1 = 1.0f; mult2 = -1.0f; } else if (random <= 2000) { - b = orange; mult1 = -1.0f; mult2 = -1.0f; } - box = b.getChild("Box" + random); + box = batchNode.getChild("Box" + random); if (box != null) { Vector3f v = box.getLocalTranslation(); - box.setLocalTranslation(v.x + FastMath.sin(time * mult1) * 20, v.y +( FastMath.sin(time * mult1)* FastMath.cos(time * mult1)* 20), v.z + FastMath.cos(time * mult2) * 20); + box.setLocalTranslation(v.x + FastMath.sin(time * mult1) * 20, v.y + (FastMath.sin(time * mult1) * FastMath.cos(time * mult1) * 20), v.z + FastMath.cos(time * mult2) * 20); } terrain.setLocalRotation(new Quaternion().fromAngleAxis(time, Vector3f.UNIT_Y)); diff --git a/engine/src/test/jme3test/batching/TestBatchNodeTower.java b/engine/src/test/jme3test/batching/TestBatchNodeTower.java index dbe5e5bdc..eb764b838 100644 --- a/engine/src/test/jme3test/batching/TestBatchNodeTower.java +++ b/engine/src/test/jme3test/batching/TestBatchNodeTower.java @@ -54,12 +54,15 @@ import com.jme3.math.Vector3f; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.BatchNode; import com.jme3.scene.Geometry; +import com.jme3.scene.SimpleBatchNode; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere.TextureMode; import com.jme3.shadow.PssmShadowRenderer; import com.jme3.shadow.PssmShadowRenderer.CompareMode; import com.jme3.shadow.PssmShadowRenderer.FilterMode; +import com.jme3.system.AppSettings; +import com.jme3.system.NanoTimer; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; @@ -90,11 +93,14 @@ public class TestBatchNodeTower extends SimpleApplication { public static void main(String args[]) { TestBatchNodeTower f = new TestBatchNodeTower(); + AppSettings s = new AppSettings(true); + f.setSettings(s); f.start(); } @Override public void simpleInitApp() { + timer = new NanoTimer(); bulletAppState = new BulletAppState(); bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL); // bulletAppState.setEnabled(false); @@ -128,7 +134,7 @@ public class TestBatchNodeTower extends SimpleApplication { bsr.setShadowIntensity(0.6f); bsr.setCompareMode(CompareMode.Hardware); bsr.setFilterMode(FilterMode.PCF4); - viewPort.addProcessor(bsr); + viewPort.addProcessor(bsr); } private PhysicsSpace getPhysicsSpace() { @@ -219,7 +225,7 @@ public class TestBatchNodeTower extends SimpleApplication { tex3.setWrap(WrapMode.Repeat); mat3.setTexture("ColorMap", tex3); } - +int nbBrick =0; public void addBrick(Vector3f ori) { Geometry reBoxg = new Geometry("brick", brick); reBoxg.setMaterial(mat); @@ -230,6 +236,7 @@ public class TestBatchNodeTower extends SimpleApplication { reBoxg.getControl(RigidBodyControl.class).setFriction(1.6f); this.batchNode.attachChild(reBoxg); this.getPhysicsSpace().add(reBoxg); + nbBrick++; } protected void initCrossHairs() {