- 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
3.0
rem..om 13 years ago
parent 2a1dc06e1d
commit 83f3b2b1a3
  1. 66
      engine/src/core/com/jme3/scene/BatchNode.java
  2. 68
      engine/src/core/com/jme3/scene/Spatial.java

@ -76,7 +76,10 @@ public class BatchNode extends Node implements Savable {
* the map of geometry holding the batched meshes * the map of geometry holding the batched meshes
*/ */
protected Map<Material, Batch> batches = new HashMap<Material, Batch>(); protected Map<Material, Batch> batches = new HashMap<Material, Batch>();
/**
* used to store transformed vectors before proceeding to a bulk put into the FloatBuffer
*/
private float[] tmpFloat;
/** /**
* Construct a batchNode * Construct a batchNode
*/ */
@ -130,7 +133,7 @@ public class BatchNode extends Node implements Savable {
assert refreshFlags == 0; assert refreshFlags == 0;
} }
protected Transform getTransforms(Geometry geom){ protected Transform getTransforms(Geometry geom) {
return geom.getWorldTransform(); return geom.getWorldTransform();
} }
@ -140,18 +143,18 @@ public class BatchNode extends Node implements Savable {
Mesh mesh = batch.geometry.getMesh(); Mesh mesh = batch.geometry.getMesh();
FloatBuffer buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Position).getData(); 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); mesh.getBuffer(VertexBuffer.Type.Position).updateData(buf);
buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Normal).getData(); // buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Normal).getData();
doTransformNorm(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat); // doTransformNorm(buf, 0, bg.startIndex, bg.startIndex + bg.getVertexCount(), buf, bg.cachedOffsetMat);
mesh.getBuffer(VertexBuffer.Type.Normal).updateData(buf); // mesh.getBuffer(VertexBuffer.Type.Normal).updateData(buf);
if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) { if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
buf = (FloatBuffer) mesh.getBuffer(VertexBuffer.Type.Tangent).getData(); 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); mesh.getBuffer(VertexBuffer.Type.Tangent).updateData(buf);
} }
@ -183,6 +186,7 @@ public class BatchNode extends Node implements Savable {
List<Geometry> list = matMap.get(material); List<Geometry> list = matMap.get(material);
nbGeoms += list.size(); nbGeoms += list.size();
mergeGeometries(m, list); mergeGeometries(m, list);
m.setDynamic();
Batch batch = new Batch(); Batch batch = new Batch();
batch.geometry = new Geometry(name + "-batch" + batches.size()); batch.geometry = new Geometry(name + "-batch" + batches.size());
@ -201,7 +205,8 @@ public class BatchNode extends Node implements Savable {
private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n) { private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n) {
if (n.getClass() == Geometry.class) { if (n.getClass() == Geometry.class) {
if (!isBatch(n)) {
if (!isBatch(n) && n.getBatchHint() != BatchHint.Never) {
Geometry g = (Geometry) n; Geometry g = (Geometry) n;
if (g.getMaterial() == null) { if (g.getMaterial() == null) {
throw new IllegalStateException("No material is set for Geometry: " + g.getName() + " please set a material before batching"); throw new IllegalStateException("No material is set for Geometry: " + g.getName() + " please set a material before batching");
@ -294,7 +299,6 @@ public class BatchNode extends Node implements Savable {
// } // }
// return null;//material; // return null;//material;
// } // }
@Override @Override
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
super.write(ex); super.write(ex);
@ -413,18 +417,22 @@ public class BatchNode extends Node implements Savable {
} }
VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]); 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); outMesh.setBuffer(vb);
} }
int globalVertIndex = 0; int globalVertIndex = 0;
int globalTriIndex = 0; int globalTriIndex = 0;
int maxVertCount = 0;
for (Geometry geom : geometries) { for (Geometry geom : geometries) {
Mesh inMesh = geom.getMesh(); Mesh inMesh = geom.getMesh();
geom.batch(this, globalVertIndex); geom.batch(this, globalVertIndex);
int geomVertCount = inMesh.getVertexCount(); int geomVertCount = inMesh.getVertexCount();
if (maxVertCount < geomVertCount) {
maxVertCount = geomVertCount;
}
int geomTriCount = inMesh.getTriangleCount(); int geomTriCount = inMesh.getTriangleCount();
for (int bufType = 0; bufType < compsForBuf.length; bufType++) { for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
@ -466,15 +474,19 @@ public class BatchNode extends Node implements Savable {
globalVertIndex += geomVertCount; globalVertIndex += geomVertCount;
globalTriIndex += geomTriCount; 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(); TempVars vars = TempVars.get();
Vector3f pos = vars.vect1; Vector3f pos = vars.vect1;
int length = (end - start) * 3;
// offset is given in element units // offset is given in element units
// convert to be in component units // convert to be in component units
offset *= 3; int offset = start * 3;
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
int index = i * 3; int index = i * 3;
@ -483,21 +495,27 @@ public class BatchNode extends Node implements Savable {
pos.z = inBuf.get(index + 2); pos.z = inBuf.get(index + 2);
transform.mult(pos, pos); transform.mult(pos, pos);
index += offset; index -= offset;
outBuf.put(index, pos.x); tmpFloat[index] = pos.x;
outBuf.put(index + 1, pos.y); tmpFloat[index + 1] = pos.y;
outBuf.put(index + 2, pos.z); tmpFloat[index + 2] = pos.z;
} }
vars.release(); 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(); TempVars vars = TempVars.get();
Vector3f pos = vars.vect1; Vector3f pos = vars.vect1;
int length = (end - start) * 3;
// offset is given in element units // offset is given in element units
// convert to be in component units // convert to be in component units
offset *= 3; int offset = start * 3;
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
int index = i * 3; int index = i * 3;
@ -506,12 +524,16 @@ public class BatchNode extends Node implements Savable {
pos.z = inBuf.get(index + 2); pos.z = inBuf.get(index + 2);
transform.multNormal(pos, pos); transform.multNormal(pos, pos);
index += offset; index -= offset;
outBuf.put(index, pos.x); tmpFloat[index] = pos.x;
outBuf.put(index + 1, pos.y); tmpFloat[index + 1] = pos.y;
outBuf.put(index + 2, pos.z); tmpFloat[index + 2] = pos.z;
} }
vars.release(); 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) { private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf) {

@ -107,31 +107,46 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
*/ */
Never; 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 * Refresh flag types
*/ */
protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms protected static final int RF_TRANSFORM = 0x01, // need light resort + combine transforms
RF_BOUND = 0x02, RF_BOUND = 0x02,
RF_LIGHTLIST = 0x04; // changes in light lists RF_LIGHTLIST = 0x04; // changes in light lists
protected CullHint cullHint = CullHint.Inherit; protected CullHint cullHint = CullHint.Inherit;
protected BatchHint batchHint = BatchHint.Inherit;
/** /**
* Spatial's bounding volume relative to the world. * Spatial's bounding volume relative to the world.
*/ */
protected BoundingVolume worldBound; protected BoundingVolume worldBound;
/** /**
* LightList * LightList
*/ */
protected LightList localLights; protected LightList localLights;
protected transient LightList worldLights; protected transient LightList worldLights;
/** /**
* This spatial's name. * This spatial's name.
*/ */
protected String name; protected String name;
// scale values // scale values
protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects; protected transient Camera.FrustumIntersect frustrumIntersects = Camera.FrustumIntersect.Intersects;
protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit; protected RenderQueue.Bucket queueBucket = RenderQueue.Bucket.Inherit;
@ -141,14 +156,12 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
protected Transform worldTransform; protected Transform worldTransform;
protected SafeArrayList<Control> controls = new SafeArrayList<Control>(Control.class); protected SafeArrayList<Control> controls = new SafeArrayList<Control>(Control.class);
protected HashMap<String, Savable> userData = null; protected HashMap<String, Savable> userData = null;
/** /**
* Used for smart asset caching * Used for smart asset caching
* *
* @see AssetKey#useSmartCache() * @see AssetKey#useSmartCache()
*/ */
protected AssetKey key; protected AssetKey key;
/** /**
* Spatial's parent, or null if it has none. * Spatial's parent, or null if it has none.
*/ */
@ -185,11 +198,11 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
this.name = name; this.name = name;
} }
public void setKey(AssetKey key){ public void setKey(AssetKey key) {
this.key = key; this.key = key;
} }
public AssetKey getKey(){ public AssetKey getKey() {
return key; return key;
} }
@ -556,7 +569,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
return; return;
} }
for (Control c : controls.getArray() ) { for (Control c : controls.getArray()) {
c.render(rm, vp); c.render(rm, vp);
} }
} }
@ -616,7 +629,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
public <T extends Control> T getControl(Class<T> controlType) { public <T extends Control> T getControl(Class<T> controlType) {
for (Control c : controls.getArray()) { for (Control c : controls.getArray()) {
if (controlType.isAssignableFrom(c.getClass())) { if (controlType.isAssignableFrom(c.getClass())) {
return (T)c; return (T) c;
} }
} }
return null; 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, * Returns this spatial's renderqueue bucket. If the mode is set to inherit,
* then the spatial gets its renderqueue bucket from its parent. * 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(name, "name", null);
capsule.write(worldBound, "world_bound", null); capsule.write(worldBound, "world_bound", null);
capsule.write(cullHint, "cull_mode", CullHint.Inherit); capsule.write(cullHint, "cull_mode", CullHint.Inherit);
capsule.write(batchHint, "batch_hint", BatchHint.Inherit);
capsule.write(queueBucket, "queue", RenderQueue.Bucket.Inherit); capsule.write(queueBucket, "queue", RenderQueue.Bucket.Inherit);
capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit); capsule.write(shadowMode, "shadow_mode", ShadowMode.Inherit);
capsule.write(localTransform, "transform", Transform.IDENTITY); capsule.write(localTransform, "transform", Transform.IDENTITY);
capsule.write(localLights, "lights", null); capsule.write(localLights, "lights", null);
// Shallow clone the controls array to convert its type. // 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); capsule.writeStringSavableMap(userData, "user_data", null);
} }
@ -1265,6 +1289,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
name = ic.readString("name", null); name = ic.readString("name", null);
worldBound = (BoundingVolume) ic.readSavable("world_bound", null); worldBound = (BoundingVolume) ic.readSavable("world_bound", null);
cullHint = ic.readEnum("cull_mode", CullHint.class, CullHint.Inherit); 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, queueBucket = ic.readEnum("queue", RenderQueue.Bucket.class,
RenderQueue.Bucket.Inherit); RenderQueue.Bucket.Inherit);
shadowMode = ic.readEnum("shadow_mode", ShadowMode.class, shadowMode = ic.readEnum("shadow_mode", ShadowMode.class,
@ -1309,6 +1334,18 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
cullHint = hint; cullHint = hint;
} }
/**
* <code>setBatchHint</code> 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 * @return the cullmode set on this Spatial
*/ */
@ -1316,6 +1353,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Asset {
return cullHint; return cullHint;
} }
/**
* @return the batchHint set on this Spatial
*/
public BatchHint getLocalBatchHint() {
return batchHint;
}
/** /**
* <code>setQueueBucket</code> determines at what phase of the * <code>setQueueBucket</code> determines at what phase of the
* rendering process this Spatial will rendered. See the * rendering process this Spatial will rendered. See the

Loading…
Cancel
Save