diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index 3db35c417..8b3ffc80e 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -174,6 +174,7 @@ public class Mesh implements Savable, Cloneable { private int vertCount = -1; private int elementCount = -1; + private int instanceCount = -1; private int maxNumWeights = -1; // only if using skeletal animation private int[] elementLengths; @@ -242,6 +243,7 @@ public class Mesh implements Savable, Cloneable { clone.vertexArrayID = -1; clone.vertCount = vertCount; clone.elementCount = elementCount; + clone.instanceCount = instanceCount; // although this could change // if the bone weight/index buffers are modified @@ -718,6 +720,17 @@ public class Mesh implements Savable, Cloneable { } } + private int computeInstanceCount() { + // Whatever the max of the base instance counts + int max = 0; + for( VertexBuffer vb : buffersList ) { + if( vb.getBaseInstanceCount() > max ) { + max = vb.getBaseInstanceCount(); + } + } + return max; + } + /** * Update the {@link #getVertexCount() vertex} and * {@link #getTriangleCount() triangle} counts for this mesh @@ -741,7 +754,8 @@ public class Mesh implements Savable, Cloneable { elementCount = computeNumElements(ib.getData().limit()); }else{ elementCount = computeNumElements(vertCount); - } + } + instanceCount = computeInstanceCount(); } /** @@ -789,6 +803,14 @@ public class Mesh implements Savable, Cloneable { public int getVertexCount(){ return vertCount; } + + /** + * Returns the number of instances this mesh contains. The instance + * count is based on any VertexBuffers with instancing set. + */ + public int getInstanceCount() { + return instanceCount; + } /** * Gets the triangle vertex positions at the given triangle index @@ -1333,6 +1355,7 @@ public class Mesh implements Savable, Cloneable { out.write(meshBound, "modelBound", null); out.write(vertCount, "vertCount", -1); out.write(elementCount, "elementCount", -1); + out.write(instanceCount, "instanceCount", -1); out.write(maxNumWeights, "max_num_weights", -1); out.write(mode, "mode", Mode.Triangles); out.write(collisionTree, "collisionTree", null); @@ -1370,6 +1393,7 @@ public class Mesh implements Savable, Cloneable { meshBound = (BoundingVolume) in.readSavable("modelBound", null); vertCount = in.readInt("vertCount", -1); elementCount = in.readInt("elementCount", -1); + instanceCount = in.readInt("instanceCount", -1); maxNumWeights = in.readInt("max_num_weights", -1); mode = in.readEnum("mode", Mode.class, Mode.Triangles); elementLengths = in.readIntArray("elementLengths", null); diff --git a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java index 203c85a3f..c67435b78 100644 --- a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java @@ -333,7 +333,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { protected Type bufType; protected Format format; protected boolean normalized = false; - protected transient boolean instanced = false; + protected int instanceSpan = 0; protected transient boolean dataSizeChanged = false; /** @@ -545,14 +545,39 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { } /** - * TODO: + * Sets the instanceSpan to 1 or 0 depending on + * the value of instanced and the existing value of + * instanceSpan. */ public void setInstanced(boolean instanced) { - this.instanced = instanced; + if( instanced && instanceSpan == 0 ) { + instanceSpan = 1; + } else if( !instanced ) { + instanceSpan = 0; + } } + /** + * Returns true if instanceSpan is more than 0 indicating + * that this vertex buffer contains per-instance data. + */ public boolean isInstanced() { - return instanced; + return instanceSpan > 0; + } + + /** + * Sets how this vertex buffer matches with rendered instances + * where 0 means no instancing at all, ie: all elements are + * per vertex. If set to 1 then each element goes with one + * instance. If set to 2 then each element goes with two + * instances and so on. + */ + public void setInstanceSpan(int i) { + this.instanceSpan = i; + } + + public int getInstanceSpan() { + return instanceSpan; } /** @@ -587,6 +612,20 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { return elements; } + /** + * Returns the number of 'instances' in this VertexBuffer. This + * is dependent on the current instanceSpan. When instanceSpan + * is 0 then 'instances' is 1. Otherwise, instances is elements * + * instanceSpan. It is possible to render a mesh with more instances + * but the instance data begins to repeat. + */ + public int getBaseInstanceCount() { + if( instanceSpan == 0 ) { + return 1; + } + return getNumElements() * instanceSpan; + } + /** * Called to initialize the data in the VertexBuffer. Must only * be called once. @@ -1009,7 +1048,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { vb.handleRef = new Object(); vb.id = -1; vb.normalized = normalized; - vb.instanced = instanced; + vb.instanceSpan = instanceSpan; vb.offset = offset; vb.stride = stride; vb.updateNeeded = true; @@ -1060,9 +1099,6 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { @Override public void write(JmeExporter ex) throws IOException { - if (instanced) { - throw new IOException("Serialization of instanced data not allowed"); - } OutputCapsule oc = ex.getCapsule(this); oc.write(components, "components", 0); @@ -1072,6 +1108,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { oc.write(normalized, "normalized", false); oc.write(offset, "offset", 0); oc.write(stride, "stride", 0); + oc.write(instanceSpan, "instanceSpan", 0); String dataName = "data" + format.name(); Buffer roData = getDataReadOnly(); @@ -1107,6 +1144,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { normalized = ic.readBoolean("normalized", false); offset = ic.readInt("offset", 0); stride = ic.readInt("stride", 0); + instanceSpan = ic.readInt("instanceSpan", 0); componentsLength = components * format.getComponentSize(); String dataName = "data" + format.name(); diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java index 23cd63ce8..e3e9393f3 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -2294,7 +2294,7 @@ public class LwjglRenderer implements Renderer { int slot = loc + i; if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) { // non-instanced -> instanced - glVertexAttribDivisorARB(slot, 1); + glVertexAttribDivisorARB(slot, vb.getInstanceSpan()); } else if (!vb.isInstanced() && attribs[slot] != null && attribs[slot].isInstanced()) { // instanced -> non-instanced glVertexAttribDivisorARB(slot, 0); @@ -2491,6 +2491,11 @@ public class LwjglRenderer implements Renderer { } private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { + + // Here while count is still passed in. Can be removed when/if + // the method is collapsed again. -pspeed + count = Math.max(mesh.getInstanceCount(), count); + VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); if (interleavedData != null && interleavedData.isUpdateNeeded()) { updateBufferData(interleavedData);