- 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
This commit is contained in:
parent
2a1dc06e1d
commit
83f3b2b1a3
@ -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…
x
Reference in New Issue
Block a user