From 83f3b2b1a3685ac103c1a9a10b9279333585c84c Mon Sep 17 00:00:00 2001 From: "rem..om" Date: Sun, 30 Oct 2011 19:25:33 +0000 Subject: [PATCH] - BatchNode now use a temp float array and bulk put data into the floatbuffer instad of iterative puts. (it's faster) - Spatials have now a BatchHint (Inherit, Never,Always) to know if they should be batched or not (use is the same as cullHint) git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8550 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- engine/src/core/com/jme3/scene/BatchNode.java | 70 +++++++++++------ engine/src/core/com/jme3/scene/Spatial.java | 76 +++++++++++++++---- 2 files changed, 106 insertions(+), 40 deletions(-) diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java index 22ecf1ee9..a0ae9a66f 100644 --- a/engine/src/core/com/jme3/scene/BatchNode.java +++ b/engine/src/core/com/jme3/scene/BatchNode.java @@ -76,7 +76,10 @@ public class BatchNode extends Node implements Savable { * the map of geometry holding the batched meshes */ protected Map batches = new HashMap(); - + /** + * used to store transformed vectors before proceeding to a bulk put into the FloatBuffer + */ + private float[] tmpFloat; /** * Construct a batchNode */ @@ -129,8 +132,8 @@ public class BatchNode extends Node implements Savable { assert refreshFlags == 0; } - - protected Transform getTransforms(Geometry geom){ + + protected Transform getTransforms(Geometry geom) { return geom.getWorldTransform(); } @@ -140,18 +143,18 @@ public class BatchNode extends Node implements Savable { 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); + doTransformVerts(buf, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); mesh.getBuffer(VertexBuffer.Type.Position).updateData(buf); - buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Normal).getData(); - doTransformNorm(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); - mesh.getBuffer(VertexBuffer.Type.Normal).updateData(buf); +// buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Normal).getData(); +// doTransformNorm(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); +// mesh.getBuffer(VertexBuffer.Type.Normal).updateData(buf); if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) { buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Tangent).getData(); - doTransformNorm(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); + doTransformNorm(buf, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); mesh.getBuffer(VertexBuffer.Type.Tangent).updateData(buf); } @@ -183,6 +186,7 @@ public class BatchNode extends Node implements Savable { List list = matMap.get(material); nbGeoms += list.size(); mergeGeometries(m, list); + m.setDynamic(); Batch batch = new Batch(); batch.geometry = new Geometry(name + "-batch" + batches.size()); @@ -201,7 +205,8 @@ public class BatchNode extends Node implements Savable { private void gatherGeomerties(Map> map, Spatial n) { if (n.getClass() == Geometry.class) { - if (!isBatch(n)) { + + if (!isBatch(n) && n.getBatchHint() != BatchHint.Never) { 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"); @@ -264,7 +269,7 @@ public class BatchNode extends Node implements Savable { } return null;//material; } - + // /** // * Sets the material to the a specific batch of this BatchNode // * @@ -294,7 +299,6 @@ public class BatchNode extends Node implements Savable { // } // return null;//material; // } - @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -413,18 +417,22 @@ public class BatchNode extends Node implements Savable { } VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]); - vb.setupData(VertexBuffer.Usage.Static, compsForBuf[i], formatForBuf[i], data); + vb.setupData(VertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data); outMesh.setBuffer(vb); } int globalVertIndex = 0; int globalTriIndex = 0; + int maxVertCount = 0; for (Geometry geom : geometries) { Mesh inMesh = geom.getMesh(); geom.batch(this, globalVertIndex); int geomVertCount = inMesh.getVertexCount(); + if (maxVertCount < geomVertCount) { + maxVertCount = geomVertCount; + } int geomTriCount = inMesh.getTriangleCount(); for (int bufType = 0; bufType < compsForBuf.length; bufType++) { @@ -466,15 +474,19 @@ public class BatchNode extends Node implements Savable { globalVertIndex += geomVertCount; globalTriIndex += geomTriCount; } + tmpFloat = new float[maxVertCount*3]; } + - private void doTransformVerts(FloatBuffer inBuf, int offset, int start, int end, FloatBuffer outBuf, Matrix4f transform) { + private void doTransformVerts(FloatBuffer inBuf, int start, int end, FloatBuffer outBuf, Matrix4f transform) { TempVars vars = TempVars.get(); Vector3f pos = vars.vect1; + int length = (end - start) * 3; + // offset is given in element units // convert to be in component units - offset *= 3; + int offset = start * 3; for (int i = start; i < end; i++) { int index = i * 3; @@ -483,21 +495,27 @@ public class BatchNode extends Node implements Savable { pos.z = inBuf.get(index + 2); transform.mult(pos, pos); - index += offset; - outBuf.put(index, pos.x); - outBuf.put(index + 1, pos.y); - outBuf.put(index + 2, pos.z); + index -= offset; + tmpFloat[index] = pos.x; + tmpFloat[index + 1] = pos.y; + tmpFloat[index + 2] = pos.z; + } vars.release(); + outBuf.position(offset); + //using bulk put as it's faster + outBuf.put(tmpFloat, 0, length); } - private void doTransformNorm(FloatBuffer inBuf, int offset, int start, int end, FloatBuffer outBuf, Matrix4f transform) { + private void doTransformNorm(FloatBuffer inBuf, int start, int end, FloatBuffer outBuf, Matrix4f transform) { TempVars vars = TempVars.get(); Vector3f pos = vars.vect1; + int length = (end - start) * 3; + // offset is given in element units // convert to be in component units - offset *= 3; + int offset = start * 3; for (int i = start; i < end; i++) { int index = i * 3; @@ -506,12 +524,16 @@ public class BatchNode extends Node implements Savable { pos.z = inBuf.get(index + 2); transform.multNormal(pos, pos); - index += offset; - outBuf.put(index, pos.x); - outBuf.put(index + 1, pos.y); - outBuf.put(index + 2, pos.z); + index -= offset; + tmpFloat[index] = pos.x; + tmpFloat[index + 1] = pos.y; + tmpFloat[index + 2] = pos.z; + } vars.release(); + outBuf.position(offset); + //using bulk put as it's faster + outBuf.put(tmpFloat, 0, length); } private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf) { diff --git a/engine/src/core/com/jme3/scene/Spatial.java b/engine/src/core/com/jme3/scene/Spatial.java index 057b01b96..951d113d4 100644 --- a/engine/src/core/com/jme3/scene/Spatial.java +++ b/engine/src/core/com/jme3/scene/Spatial.java @@ -107,31 +107,46 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { */ Never; } + + /** + * Specifies is this spatial should be batched + */ + public enum BatchHint { + + /** + * Do whatever our parent does. If no parent, default to {@link #Always}. + */ + Inherit, + /** + * this spatial will always be batched when attached to a BatchNode + */ + Always, + /** + * this spatial will naver be batched when attached to a BatchNode + */ + Never; + } /** * Refresh flag types */ protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms RF_BOUND = 0x02, RF_LIGHTLIST = 0x04; // changes in light lists - protected CullHint cullHint = CullHint.Inherit; - + protected BatchHint batchHint = BatchHint.Inherit; /** * Spatial's bounding volume relative to the world. */ protected BoundingVolume worldBound; - /** * LightList */ protected LightList localLights; protected transient LightList worldLights; - /** * This spatial's name. */ protected String name; - // scale values protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects; protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit; @@ -141,14 +156,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { protected Transform worldTransform; protected SafeArrayList controls = new SafeArrayList(Control.class); protected HashMap userData = null; - /** * Used for smart asset caching * * @see AssetKey#useSmartCache() */ - protected AssetKey key; - + protected AssetKey key; /** * Spatial's parent, or null if it has none. */ @@ -185,14 +198,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { this.name = name; } - public void setKey(AssetKey key){ + public void setKey(AssetKey key) { this.key = key; } - - public AssetKey getKey(){ + + public AssetKey getKey() { return key; } - + /** * Indicate that the transform of this spatial has changed and that * a refresh is required. @@ -556,7 +569,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { return; } - for (Control c : controls.getArray() ) { + for (Control c : controls.getArray()) { c.render(rm, vp); } } @@ -616,7 +629,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { public T getControl(Class controlType) { for (Control c : controls.getArray()) { if (controlType.isAssignableFrom(c.getClass())) { - return (T)c; + return (T) c; } } return null; @@ -1027,6 +1040,16 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { } } + public BatchHint getBatchHint() { + if (batchHint != BatchHint.Inherit) { + return batchHint; + } else if (parent != null) { + return parent.getBatchHint(); + } else { + return BatchHint.Always; + } + } + /** * Returns this spatial's renderqueue bucket. If the mode is set to inherit, * then the spatial gets its renderqueue bucket from its parent. @@ -1249,13 +1272,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { capsule.write(name, "name", null); capsule.write(worldBound, "world_bound", null); capsule.write(cullHint, "cull_mode", CullHint.Inherit); + capsule.write(batchHint, "batch_hint", BatchHint.Inherit); capsule.write(queueBucket, "queue", RenderQueue.Bucket.Inherit); capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit); capsule.write(localTransform, "transform", Transform.IDENTITY); capsule.write(localLights, "lights", null); - + // Shallow clone the controls array to convert its type. - capsule.writeSavableArrayList( new ArrayList(controls), "controlsList", null); + capsule.writeSavableArrayList(new ArrayList(controls), "controlsList", null); capsule.writeStringSavableMap(userData, "user_data", null); } @@ -1265,6 +1289,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { name = ic.readString("name", null); worldBound = (BoundingVolume) ic.readSavable("world_bound", null); cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit); + batchHint = ic.readEnum("batch_hint", BatchHint.class, BatchHint.Inherit); queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class, RenderQueue.Bucket.Inherit); shadowMode = ic.readEnum("shadow_mode", ShadowMode.class, @@ -1309,6 +1334,18 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { cullHint = hint; } + /** + * setBatchHint sets how batching should work on this + * spatial. NOTE: You must set this AFTER attaching to a + * parent or it will be reset with the parent's cullMode value. + * + * @param hint + * one of BatchHint.Never, BatchHint.Always, BatchHint.Inherit + */ + public void setBatchHint(BatchHint hint) { + batchHint = hint; + } + /** * @return the cullmode set on this Spatial */ @@ -1316,6 +1353,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset { return cullHint; } + /** + * @return the batchHint set on this Spatial + */ + public BatchHint getLocalBatchHint() { + return batchHint; + } + /** * setQueueBucket determines at what phase of the * rendering process this Spatial will rendered. See the