* Optimization to prevent allocation of IntMap$Iterator in Renderer.renderMesh()

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7661 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent bc2de8626d
commit f9079171f1
  1. 343
      engine/src/core/com/jme3/scene/Mesh.java
  2. 48
      engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java

@ -46,6 +46,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.InputCapsule;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.material.RenderState;
import com.jme3.math.Matrix4f;
import com.jme3.math.Triangle;
import com.jme3.math.Vector2f;
@ -65,22 +66,85 @@ import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
/**
* <code>Mesh</code> is used to store rendering data.
* <p>
* All visible elements in a scene are represented by meshes.
* Meshes may contain three types of geometric primitives:
* <ul>
* <li>Points</li>
* <li>Lines</li>
* <li>Triangles</li>
* </ul>
*
* @author Kirill Vainer
*/
public class Mesh implements Savable, Cloneable {
// TODO: Document this enum
/**
* The mode of the Mesh specifies both the type of primitive represented
* by the mesh and how the data should be interpreted.
*/
public enum Mode {
/**
* A primitive is a single point in space. The size of the points
* can be specified with {@link Mesh#setPointSize(float) }.
*/
Points,
/**
* A primitive is a line segment. Every two vertices specify
* a single line. {@link Mesh#setLineWidth(float) } can be used
* to set the width of the lines.
*/
Lines,
LineLoop,
/**
* A primitive is a line segment. The first two vertices specify
* a single line, while subsequent vertices are combined with the
* previous vertex to make a line. {@link Mesh#setLineWidth(float) } can
* be used to set the width of the lines.
*/
LineStrip,
/**
* Identical to {@link #LineStrip} except that at the end
* the last vertex is connected with the first to form a line.
* {@link Mesh#setLineWidth(float) } can be used
* to set the width of the lines.
*/
LineLoop,
/**
* A primitive is a triangle. Each 3 vertices specify a single
* triangle.
*/
Triangles,
/**
* Similar to {@link #Triangles}, the first 3 vertices
* specify a triangle, while subsequent vertices are combined with
* the previous two to form a triangle.
*/
TriangleStrip,
/**
* Similar to {@link #Triangles}, the first 3 vertices
* specify a triangle, each 2 subsequent vertices are combined
* with the very first vertex to make a triangle.
*/
TriangleFan,
Hybrid
/**
* A combination of various triangle modes. It is best to avoid
* using this mode as it may not be supported by all renderers.
* The {@link Mesh#setModeStart(int[]) mode start points} and
* {@link Mesh#setElementLengths(int[]) element lengths} must
* be specified for this mode.
*/
Hybrid;
}
// private static final int BUFFERS_SIZE = VertexBuffer.Type.BoneIndex.ordinal() + 1;
/**
* The bounding volume that contains the mesh entirely.
* By default a BoundingBox (AABB).
@ -89,6 +153,7 @@ public class Mesh implements Savable, Cloneable {
private CollisionData collisionTree = null;
private ArrayList<VertexBuffer> buffersList = new ArrayList<VertexBuffer>(5);
private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
private VertexBuffer[] lodLevels;
private float pointSize = 1;
@ -105,27 +170,47 @@ public class Mesh implements Savable, Cloneable {
private Mode mode = Mode.Triangles;
/**
* Creates a new mesh with no {@link VertexBuffer vertex buffers}.
*/
public Mesh(){
}
/**
* Create a shallow clone of this Mesh. The {@link VertexBuffer vertex
* buffers} are shared between this and the clone mesh, the rest
* of the data is cloned.
*
* @return A shallow clone of the mesh
*/
@Override
public Mesh clone(){
try{
public Mesh clone() {
try {
Mesh clone = (Mesh) super.clone();
clone.meshBound = meshBound.clone();
clone.collisionTree = collisionTree != null ? collisionTree : null;
clone.buffers = buffers.clone();
clone.buffersList = new ArrayList<VertexBuffer>(buffersList);
clone.vertexArrayID = -1;
if (elementLengths != null)
if (elementLengths != null) {
clone.elementLengths = elementLengths.clone();
if (modeStart != null)
}
if (modeStart != null) {
clone.modeStart = modeStart.clone();
}
return clone;
}catch (CloneNotSupportedException ex){
} catch (CloneNotSupportedException ex) {
throw new AssertionError();
}
}
/**
* Creates a deep clone of this mesh.
* The {@link VertexBuffer vertex buffers} and the data inside them
* is cloned.
*
* @return a deep clone of this mesh.
*/
public Mesh deepClone(){
try{
Mesh clone = (Mesh) super.clone();
@ -136,13 +221,21 @@ public class Mesh implements Savable, Cloneable {
clone.collisionTree = null; // it will get re-generated in any case
clone.buffers = new IntMap<VertexBuffer>();
clone.buffersList = new ArrayList<VertexBuffer>();
for (Entry<VertexBuffer> ent : buffers){
clone.buffers.put(ent.getKey(), ent.getValue().clone());
VertexBuffer bufClone = ent.getValue().clone();
clone.buffers.put(ent.getKey(), bufClone);
clone.buffersList.add(bufClone);
}
clone.vertexArrayID = -1;
clone.vertCount = -1;
clone.elementCount = -1;
clone.maxNumWeights = -1;
// although this could change
// if the bone weight/index buffers are modified
clone.maxNumWeights = maxNumWeights;
clone.elementLengths = elementLengths != null ? elementLengths.clone() : null;
clone.modeStart = modeStart != null ? modeStart.clone() : null;
return clone;
@ -151,6 +244,15 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Clone the mesh for animation use.
* This creates a shallow clone of the mesh, sharing most
* of the {@link VertexBuffer vertex buffer} data, however the
* {@link Type#BindPosePosition} and {@link Type#BindPoseNormal} buffers
* are deeply cloned.
*
* @return A clone of the mesh for animation use.
*/
public Mesh cloneForAnim(){
Mesh clone = clone();
if (getBuffer(Type.BindPosePosition) != null){
@ -170,8 +272,17 @@ public class Mesh implements Savable, Cloneable {
return clone;
}
public void generateBindPose(boolean swAnim){
if (swAnim){
/**
* Generates the {@link Type#BindPosePosition} and {@link Type#BindPoseNormal}
* buffers for this mesh by duplicating them based on the position and normal
* buffers already set on the mesh.
* This method does nothing if the mesh has no bone weight or index
* buffers.
*
* @param forSoftwareAnim Should be true if the bind pose is to be generated.
*/
public void generateBindPose(boolean forSoftwareAnim){
if (forSoftwareAnim){
VertexBuffer pos = getBuffer(Type.Position);
if (pos == null || getBuffer(Type.BoneIndex) == null) {
// ignore, this mesh doesn't have positional data
@ -200,13 +311,17 @@ public class Mesh implements Savable, Cloneable {
setBuffer(bindNorm);
norm.setUsage(Usage.Stream);
}
norm.setUsage(Usage.Stream);
}
}
public void prepareForAnim(boolean swAnim){
if (swAnim){
/**
* Prepares the mesh for software skinning by converting the bone index
* and weight buffers to heap buffers.
*
* @param forSoftwareAnim Should be true to enable the conversion.
*/
public void prepareForAnim(boolean forSoftwareAnim){
if (forSoftwareAnim){
// convert indices
VertexBuffer indices = getBuffer(Type.BoneIndex);
ByteBuffer originalIndex = (ByteBuffer) indices.getData();
@ -225,6 +340,11 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Set the LOD (level of detail) index buffers on this mesh.
*
* @param lodLevels The LOD levels to set
*/
public void setLodLevels(VertexBuffer[] lodLevels){
this.lodLevels = lodLevels;
}
@ -237,62 +357,152 @@ public class Mesh implements Savable, Cloneable {
return lodLevels != null ? lodLevels.length : 0;
}
/**
* Returns the lod level at the given index.
*
* @param lod The lod level index, this does not include
* the main index buffer.
* @return The LOD index buffer at the index
*
* @throws IndexOutOfBoundsException If the index is outside of the
* range [0, {@link #getNumLodLevels()}].
*
* @see #setLodLevels(com.jme3.scene.VertexBuffer[])
*/
public VertexBuffer getLodLevel(int lod){
return lodLevels[lod];
}
/**
* Get the element lengths for {@link Mode#Hybrid} mesh mode.
*
* @return element lengths
*/
public int[] getElementLengths() {
return elementLengths;
}
/**
* Set the element lengths for {@link Mode#Hybrid} mesh mode.
*
* @param elementLengths The element lengths to set
*/
public void setElementLengths(int[] elementLengths) {
this.elementLengths = elementLengths;
}
/**
* Set the mode start indices for {@link Mode#Hybrid} mesh mode.
*
* @return mode start indices
*/
public int[] getModeStart() {
return modeStart;
}
/**
* Get the mode start indices for {@link Mode#Hybrid} mesh mode.
*
* @return mode start indices
*/
public void setModeStart(int[] modeStart) {
this.modeStart = modeStart;
}
/**
* Returns the mesh mode
*
* @return the mesh mode
*
* @see #setMode(com.jme3.scene.Mesh.Mode)
*/
public Mode getMode() {
return mode;
}
/**
* Change the Mesh's mode. By default the mode is {@link Mode#Triangles}.
*
* @param mode The new mode to set
*
* @see Mode
*/
public void setMode(Mode mode) {
this.mode = mode;
updateCounts();
}
/**
* Returns the maximum number of weights per vertex on this mesh.
*
* @return maximum number of weights per vertex
*
* @see #setMaxNumWeights(int)
*/
public int getMaxNumWeights() {
return maxNumWeights;
}
/**
* Set the maximum number of weights per vertex on this mesh.
* Only relevant if this mesh has bone index/weight buffers.
* This value should be between 0 and 4.
*
* @param maxNumWeights
*/
public void setMaxNumWeights(int maxNumWeights) {
this.maxNumWeights = maxNumWeights;
}
/**
* Returns the size of points for point meshes
*
* @return the size of points
*
* @see #setPointSize(float)
*/
public float getPointSize() {
return pointSize;
}
/**
* Set the size of points for meshes of mode {@link Mode#Points}.
* The point size is specified as on-screen pixels, the default
* value is 1.0. The point size
* does nothing if {@link RenderState#setPointSprite(boolean) point sprite}
* render state is enabled, in that case, the vertex shader must specify the
* point size by writing to <code>gl_PointSize</code>.
*
* @param pointSize The size of points
*/
public void setPointSize(float pointSize) {
this.pointSize = pointSize;
}
/**
* Returns the line width for line meshes.
*
* @return the line width
*/
public float getLineWidth() {
return lineWidth;
}
/**
* Specify the line width for meshes of the line modes, such
* as {@link Mode#Lines}. The line width is specified as on-screen pixels,
* the default value is 1.0.
*
* @param lineWidth The line width
*/
public void setLineWidth(float lineWidth) {
this.lineWidth = lineWidth;
}
/**
* Locks the mesh so it cannot be modified anymore, thus
* optimizing its data.
* Indicates to the GPU that this mesh will not be modified (a hint).
* Sets the usage mode to {@link Usage#Static}
* for all {@link VertexBuffer vertex buffers} on this Mesh.
*/
public void setStatic() {
for (Entry<VertexBuffer> entry : buffers){
@ -301,8 +511,9 @@ public class Mesh implements Savable, Cloneable {
}
/**
* Unlocks the mesh so it can be modified, this
* will un-optimize the data!
* Indicates to the GPU that this mesh will be modified occasionally (a hint).
* Sets the usage mode to {@link Usage#Dynamic}
* for all {@link VertexBuffer vertex buffers} on this Mesh.
*/
public void setDynamic() {
for (Entry<VertexBuffer> entry : buffers){
@ -310,12 +521,22 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Indicates to the GPU that this mesh will be modified every frame (a hint).
* Sets the usage mode to {@link Usage#Stream}
* for all {@link VertexBuffer vertex buffers} on this Mesh.
*/
public void setStreamed(){
for (Entry<VertexBuffer> entry : buffers){
entry.getValue().setUsage(Usage.Stream);
}
}
/**
* Interleaves the data in this mesh. This operation cannot be reversed.
* Some GPUs may prefer the data in this format, however it is a good idea
* to <em>avoid</em> using this method as it disables some engine features.
*/
public void setInterleaved(){
ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>();
for (Entry<VertexBuffer> entry : buffers){
@ -339,8 +560,10 @@ public class Mesh implements Savable, Cloneable {
VertexBuffer allData = new VertexBuffer(Type.InterleavedData);
ByteBuffer dataBuf = BufferUtils.createByteBuffer(stride * getVertexCount());
allData.setupData(Usage.Static, 1, Format.UnsignedByte, dataBuf);
// adding buffer directly so that no update counts is forced
buffers.put(Type.InterleavedData.ordinal(), allData);
buffersList.add(allData);
for (int vert = 0; vert < getVertexCount(); vert++){
for (int i = 0; i < vbs.size(); i++){
@ -415,6 +638,16 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Update the {@link #getVertexCount() vertex} and
* {@link #getTriangleCount() triangle} counts for this mesh
* based on the current data. This method should be called
* after the {@link Buffer#capacity() capacities} of the mesh's
* {@link VertexBuffer vertex buffers} has been altered.
*
* @throws IllegalStateException If this mesh is in
* {@link #setInterleaved() interleaved} format.
*/
public void updateCounts(){
if (getBuffer(Type.InterleavedData) != null)
throw new IllegalStateException("Should update counts before interleave");
@ -431,6 +664,12 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Returns the triangle count for the given LOD level.
*
* @param lod The lod level to look up
* @return The triangle count for that LOD level
*/
public int getTriangleCount(int lod){
if (lodLevels != null){
if (lod < 0)
@ -447,10 +686,17 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Returns how many triangles are on this Mesh.
* This value is only updated when {@link #updateCounts() } is called.
*
* @return how many triangles are on this Mesh.
*/
public int getTriangleCount(){
return elementCount;
}
public int getVertexCount(){
return vertCount;
}
@ -540,6 +786,7 @@ public class Mesh implements Savable, Cloneable {
vb.setupData(Usage.Dynamic, components, Format.Float, buf);
// buffers.put(type, vb);
buffers.put(type.ordinal(), vb);
buffersList.add(vb);
}else{
vb.setupData(Usage.Dynamic, components, Format.Float, buf);
}
@ -556,6 +803,7 @@ public class Mesh implements Savable, Cloneable {
vb = new VertexBuffer(type);
vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf);
buffers.put(type.ordinal(), vb);
buffersList.add(vb);
updateCounts();
}
}
@ -570,6 +818,7 @@ public class Mesh implements Savable, Cloneable {
vb = new VertexBuffer(type);
vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf);
buffers.put(type.ordinal(), vb);
buffersList.add(vb);
updateCounts();
}
}
@ -584,6 +833,7 @@ public class Mesh implements Savable, Cloneable {
vb = new VertexBuffer(type);
vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf);
buffers.put(type.ordinal(), vb);
buffersList.add(vb);
updateCounts();
}
}
@ -593,12 +843,16 @@ public class Mesh implements Savable, Cloneable {
throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
buffers.put(vb.getBufferType().ordinal(), vb);
buffersList.add(vb);
updateCounts();
}
public void clearBuffer(VertexBuffer.Type type){
buffers.remove(type.ordinal());
updateCounts();
VertexBuffer vb = buffers.remove(type.ordinal());
if (vb != null){
buffersList.remove(vb);
updateCounts();
}
}
public void setBuffer(Type type, int components, short[] buf){
@ -642,6 +896,13 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Scales the texture coordinate buffer on this mesh by the given
* scale factor.
*
*
* @param scaleFactor
*/
public void scaleTextureCoordinates(Vector2f scaleFactor){
VertexBuffer tc = getBuffer(Type.TexCoord);
if (tc == null)
@ -667,6 +928,11 @@ public class Mesh implements Savable, Cloneable {
tc.updateData(fb);
}
/**
* Updates the bounding volume of this mesh.
* The method does nothing if the mesh has no {@link Type#Position} buffer.
* It is expected that the position buffer is a float buffer with 3 components.
*/
public void updateBound(){
VertexBuffer posBuf = getBuffer(VertexBuffer.Type.Position);
if (meshBound != null && posBuf != null){
@ -674,17 +940,42 @@ public class Mesh implements Savable, Cloneable {
}
}
/**
* Returns the {@link BoundingVolume} of this Mesh.
* By default the bounding volume is a {@link BoundingBox}.
*
* @return the bounding volume of this mesh
*/
public BoundingVolume getBound() {
return meshBound;
}
/**
* Sets the {@link BoundingVolume} for this Mesh.
* The bounding volume is recomputed by calling {@link #updateBound() }.
*
* @param modelBound The model bound to set
*/
public void setBound(BoundingVolume modelBound) {
meshBound = modelBound;
}
/**
* Returns a map of all {@link VertexBuffer vertex buffers} on this Mesh.
* The integer key for the map is the {@link Enum#ordinal() ordinal}
* of the vertex buffer's {@link Type}.
* Note that the returned map is a reference to the map used internally,
* modifying it will cause undefined results.
*
* @return map of vertex buffers on this mesh.
*/
public IntMap<VertexBuffer> getBuffers(){
return buffers;
}
public ArrayList<VertexBuffer> getBufferList(){
return buffersList;
}
public void write(JmeExporter ex) throws IOException {
OutputCapsule out = ex.getCapsule(this);
@ -726,6 +1017,10 @@ public class Mesh implements Savable, Cloneable {
// in.readStringSavableMap("buffers", null);
buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null);
for (Entry<VertexBuffer> entry : buffers){
buffersList.add(entry.getValue());
}
Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null);
if (lodLevelsSavable != null) {
lodLevels = new VertexBuffer[lodLevelsSavable.length];

@ -75,6 +75,7 @@ import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
@ -478,6 +479,14 @@ public class LwjglRenderer implements Renderer {
public void setBackgroundColor(ColorRGBA color) {
glClearColor(color.r, color.g, color.b, color.a);
}
public void setAlphaToCoverage(boolean value) {
if (value) {
glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
} else {
glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
}
}
public void applyRenderState(RenderState state) {
if (state.isWireframe() && !context.wireframe) {
@ -2260,8 +2269,10 @@ public class LwjglRenderer implements Renderer {
}
private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
if (mesh.getId() == -1) {
if (mesh.getId() == -1){
updateVertexArray(mesh);
}else{
// TODO: Check if it was updated
}
if (context.boundVertexArray != mesh.getId()) {
@ -2269,18 +2280,17 @@ public class LwjglRenderer implements Renderer {
context.boundVertexArray = mesh.getId();
}
IntMap<VertexBuffer> buffers = mesh.getBuffers();
// IntMap<VertexBuffer> buffers = mesh.getBuffers();
VertexBuffer indices = null;
if (mesh.getNumLodLevels() > 0) {
indices = mesh.getLodLevel(lod);
} else {
indices = buffers.get(Type.Index.ordinal());
indices = mesh.getBuffer(Type.Index);
}
if (indices != null) {
drawTriangleList(indices, mesh, count);
} else {
// throw new UnsupportedOperationException("Cannot render without index buffer");
glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
}
clearVertexAttribs();
clearTextureUnits();
@ -2294,15 +2304,19 @@ public class LwjglRenderer implements Renderer {
updateBufferData(interleavedData);
}
IntMap<VertexBuffer> buffers = mesh.getBuffers();
//IntMap<VertexBuffer> buffers = mesh.getBuffers();
ArrayList<VertexBuffer> buffersList = mesh.getBufferList();
if (mesh.getNumLodLevels() > 0) {
indices = mesh.getLodLevel(lod);
} else {
indices = buffers.get(Type.Index.ordinal());
indices = mesh.getBuffer(Type.Index);
}
for (Entry<VertexBuffer> entry : buffers) {
VertexBuffer vb = entry.getValue();
//for (Entry<VertexBuffer> entry : buffers) {
// VertexBuffer vb = entry.getValue();
for (int i = 0; i < buffersList.size(); i++){
VertexBuffer vb = buffersList.get(i);
if (vb.getBufferType() == Type.InterleavedData
|| vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
|| vb.getBufferType() == Type.Index) {
@ -2317,11 +2331,11 @@ public class LwjglRenderer implements Renderer {
setVertexAttrib(vb, interleavedData);
}
}
if (indices != null) {
drawTriangleList(indices, mesh, count);
} else {
// throw new UnsupportedOperationException("Cannot render without index buffer");
glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
}
clearVertexAttribs();
clearTextureUnits();
@ -2344,15 +2358,7 @@ public class LwjglRenderer implements Renderer {
// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
// renderMeshVertexArray(mesh, lod, count);
// }else{
renderMeshDefault(mesh, lod, count);
renderMeshDefault(mesh, lod, count);
// }
}
public void setAlphaToCoverage(boolean value) {
if (value) {
glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
} else {
glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
}
}
}

Loading…
Cancel
Save