aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/scene/VertexBuffer.java
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/core/com/jme3/scene/VertexBuffer.java')
-rw-r--r--engine/src/core/com/jme3/scene/VertexBuffer.java1025
1 files changed, 1025 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/scene/VertexBuffer.java b/engine/src/core/com/jme3/scene/VertexBuffer.java
new file mode 100644
index 0000000..1a844bb
--- /dev/null
+++ b/engine/src/core/com/jme3/scene/VertexBuffer.java
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.scene;
+
+import com.jme3.export.*;
+import com.jme3.math.FastMath;
+import com.jme3.renderer.Renderer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.NativeObject;
+import java.io.IOException;
+import java.nio.*;
+
+/**
+ * A <code>VertexBuffer</code> contains a particular type of geometry
+ * data used by {@link Mesh}es. Every VertexBuffer set on a <code>Mesh</code>
+ * is sent as an attribute to the vertex shader to be processed.
+ * <p>
+ * Several terms are used throughout the javadoc for this class, explanation:
+ * <ul>
+ * <li>Element - A single element is the largest individual object
+ * inside a VertexBuffer. E.g. if the VertexBuffer is used to store 3D position
+ * data, then an element will be a single 3D vector.</li>
+ * <li>Component - A component represents the parts inside an element.
+ * For a 3D vector, a single component is one of the dimensions, X, Y or Z.</li>
+ * </ul>
+ */
+public class VertexBuffer extends NativeObject implements Savable, Cloneable {
+
+ /**
+ * Type of buffer. Specifies the actual attribute it defines.
+ */
+ public static enum Type {
+ /**
+ * Position of the vertex (3 floats)
+ */
+ Position,
+
+ /**
+ * The size of the point when using point buffers (float).
+ */
+ Size,
+
+ /**
+ * Normal vector, normalized (3 floats).
+ */
+ Normal,
+
+ /**
+ * Texture coordinate (2 float)
+ */
+ TexCoord,
+
+ /**
+ * Color and Alpha (4 floats)
+ */
+ Color,
+
+ /**
+ * Tangent vector, normalized (4 floats) (x,y,z,w)
+ * the w component is called the binormal parity, is not normalized and is either 1f or -1f
+ * It's used to compuste the direction on the binormal verctor on the GPU at render time.
+ */
+ Tangent,
+
+ /**
+ * Binormal vector, normalized (3 floats, optional)
+ */
+ Binormal,
+
+ /**
+ * Specifies the source data for various vertex buffers
+ * when interleaving is used. By default the format is
+ * byte.
+ */
+ InterleavedData,
+
+ /**
+ * Do not use.
+ */
+ @Deprecated
+ MiscAttrib,
+
+ /**
+ * Specifies the index buffer, must contain integer data
+ * (ubyte, ushort, or uint).
+ */
+ Index,
+
+ /**
+ * Initial vertex position, used with animation.
+ * Should have the same format and size as {@link Type#Position}.
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BindPosePosition,
+
+ /**
+ * Initial vertex normals, used with animation.
+ * Should have the same format and size as {@link Type#Normal}.
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BindPoseNormal,
+
+ /**
+ * Bone weights, used with animation (4 floats).
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BoneWeight,
+
+ /**
+ * Bone indices, used with animation (4 ubytes).
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BoneIndex,
+
+ /**
+ * Texture coordinate #2
+ */
+ TexCoord2,
+
+ /**
+ * Texture coordinate #3
+ */
+ TexCoord3,
+
+ /**
+ * Texture coordinate #4
+ */
+ TexCoord4,
+
+ /**
+ * Texture coordinate #5
+ */
+ TexCoord5,
+
+ /**
+ * Texture coordinate #6
+ */
+ TexCoord6,
+
+ /**
+ * Texture coordinate #7
+ */
+ TexCoord7,
+
+ /**
+ * Texture coordinate #8
+ */
+ TexCoord8,
+
+ /**
+ * Initial vertex tangents, used with animation.
+ * Should have the same format and size as {@link Type#Tangent}.
+ * If used with software skinning, the usage should be
+ * {@link Usage#CpuOnly}, and the buffer should be allocated
+ * on the heap.
+ */
+ BindPoseTangent,
+ }
+
+ /**
+ * The usage of the VertexBuffer, specifies how often the buffer
+ * is used. This can determine if a vertex buffer is placed in VRAM
+ * or held in video memory, but no guarantees are made- it's only a hint.
+ */
+ public static enum Usage {
+
+ /**
+ * Mesh data is sent once and very rarely updated.
+ */
+ Static,
+
+ /**
+ * Mesh data is updated occasionally (once per frame or less).
+ */
+ Dynamic,
+
+ /**
+ * Mesh data is updated every frame.
+ */
+ Stream,
+
+ /**
+ * Mesh data is <em>not</em> sent to GPU at all. It is only
+ * used by the CPU.
+ */
+ CpuOnly;
+ }
+
+ /**
+ * Specifies format of the data stored in the buffer.
+ * This should directly correspond to the buffer's class, for example,
+ * an {@link Format#UnsignedShort} formatted buffer should use the
+ * class {@link ShortBuffer} (e.g. the closest resembling type).
+ * For the {@link Format#Half} type, {@link ByteBuffer}s should
+ * be used.
+ */
+ public static enum Format {
+ /**
+ * Half precision floating point.
+ * 2 bytes, signed.
+ */
+ Half(2),
+
+ /**
+ * Single precision floating point.
+ * 4 bytes, signed
+ */
+ Float(4),
+
+ /**
+ * Double precision floating point.
+ * 8 bytes, signed. May not
+ * be supported by all GPUs.
+ */
+ Double(8),
+
+ /**
+ * 1 byte integer, signed.
+ */
+ Byte(1),
+
+ /**
+ * 1 byte integer, unsigned.
+ */
+ UnsignedByte(1),
+
+ /**
+ * 2 byte integer, signed.
+ */
+ Short(2),
+
+ /**
+ * 2 byte integer, unsigned.
+ */
+ UnsignedShort(2),
+
+ /**
+ * 4 byte integer, signed.
+ */
+ Int(4),
+
+ /**
+ * 4 byte integer, unsigned.
+ */
+ UnsignedInt(4);
+
+ private int componentSize = 0;
+
+ Format(int componentSize){
+ this.componentSize = componentSize;
+ }
+
+ /**
+ * Returns the size in bytes of this data type.
+ *
+ * @return Size in bytes of this data type.
+ */
+ public int getComponentSize(){
+ return componentSize;
+ }
+ }
+
+ protected int offset = 0;
+ protected int lastLimit = 0;
+ protected int stride = 0;
+ protected int components = 0;
+
+ /**
+ * derived from components * format.getComponentSize()
+ */
+ protected transient int componentsLength = 0;
+ protected Buffer data = null;
+ protected Usage usage;
+ protected Type bufType;
+ protected Format format;
+ protected boolean normalized = false;
+ protected transient boolean dataSizeChanged = false;
+
+ /**
+ * Creates an empty, uninitialized buffer.
+ * Must call setupData() to initialize.
+ */
+ public VertexBuffer(Type type){
+ super(VertexBuffer.class);
+ this.bufType = type;
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public VertexBuffer(){
+ super(VertexBuffer.class);
+ }
+
+ protected VertexBuffer(int id){
+ super(VertexBuffer.class, id);
+ }
+
+ /**
+ * @return The offset after which the data is sent to the GPU.
+ *
+ * @see #setOffset(int)
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * @param offset Specify the offset (in bytes) from the start of the buffer
+ * after which the data is sent to the GPU.
+ */
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * @return The stride (in bytes) for the data.
+ *
+ * @see #setStride(int)
+ */
+ public int getStride() {
+ return stride;
+ }
+
+ /**
+ * Set the stride (in bytes) for the data.
+ * <p>
+ * If the data is packed in the buffer, then stride is 0, if there's other
+ * data that is between the current component and the next component in the
+ * buffer, then this specifies the size in bytes of that additional data.
+ *
+ * @param stride the stride (in bytes) for the data
+ */
+ public void setStride(int stride) {
+ this.stride = stride;
+ }
+
+ /**
+ * Returns the raw internal data buffer used by this VertexBuffer.
+ * This buffer is not safe to call from multiple threads since buffers
+ * have their own internal position state that cannot be shared.
+ * Call getData().duplicate(), getData().asReadOnlyBuffer(), or
+ * the more convenient getDataReadOnly() if the buffer may be accessed
+ * from multiple threads.
+ *
+ * @return A native buffer, in the specified {@link Format format}.
+ */
+ public Buffer getData(){
+ return data;
+ }
+
+ /**
+ * Returns a safe read-only version of this VertexBuffer's data. The
+ * contents of the buffer will reflect whatever changes are made on
+ * other threads (eventually) but these should not be used in that way.
+ * This method provides a read-only buffer that is safe to _read_ from
+ * a separate thread since it has its own book-keeping state (position, limit, etc.)
+ *
+ * @return A rewound native buffer in the specified {@link Format format}
+ * that is safe to read from a separate thread from other readers.
+ */
+ public Buffer getDataReadOnly() {
+
+ if (data == null) {
+ return null;
+ }
+
+ // Create a read-only duplicate(). Note: this does not copy
+ // the underlying memory, it just creates a new read-only wrapper
+ // with its own buffer position state.
+
+ // Unfortunately, this is not 100% straight forward since Buffer
+ // does not have an asReadOnlyBuffer() method.
+ Buffer result;
+ if( data instanceof ByteBuffer ) {
+ result = ((ByteBuffer)data).asReadOnlyBuffer();
+ } else if( data instanceof FloatBuffer ) {
+ result = ((FloatBuffer)data).asReadOnlyBuffer();
+ } else if( data instanceof ShortBuffer ) {
+ result = ((ShortBuffer)data).asReadOnlyBuffer();
+ } else if( data instanceof IntBuffer ) {
+ result = ((IntBuffer)data).asReadOnlyBuffer();
+ } else {
+ throw new UnsupportedOperationException( "Cannot get read-only view of buffer type:" + data );
+ }
+
+ // Make sure the caller gets a consistent view since we may
+ // have grabbed this buffer while another thread was reading
+ // the raw data.
+ result.rewind();
+
+ return result;
+ }
+
+ /**
+ * @return The usage of this buffer. See {@link Usage} for more
+ * information.
+ */
+ public Usage getUsage(){
+ return usage;
+ }
+
+ /**
+ * @param usage The usage of this buffer. See {@link Usage} for more
+ * information.
+ */
+ public void setUsage(Usage usage){
+// if (id != -1)
+// throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
+
+ this.usage = usage;
+ }
+
+ /**
+ * @param normalized Set to true if integer components should be converted
+ * from their maximal range into the range 0.0 - 1.0 when converted to
+ * a floating-point value for the shader.
+ * E.g. if the {@link Format} is {@link Format#UnsignedInt}, then
+ * the components will be converted to the range 0.0 - 1.0 by dividing
+ * every integer by 2^32.
+ */
+ public void setNormalized(boolean normalized){
+ this.normalized = normalized;
+ }
+
+ /**
+ * @return True if integer components should be converted to the range 0-1.
+ * @see VertexBuffer#setNormalized(boolean)
+ */
+ public boolean isNormalized(){
+ return normalized;
+ }
+
+ /**
+ * @return The type of information that this buffer has.
+ */
+ public Type getBufferType(){
+ return bufType;
+ }
+
+ /**
+ * @return The {@link Format format}, or data type of the data.
+ */
+ public Format getFormat(){
+ return format;
+ }
+
+ /**
+ * @return The number of components of the given {@link Format format} per
+ * element.
+ */
+ public int getNumComponents(){
+ return components;
+ }
+
+ /**
+ * @return The total number of data elements in the data buffer.
+ */
+ public int getNumElements(){
+ int elements = data.capacity() / components;
+ if (format == Format.Half)
+ elements /= 2;
+ return elements;
+ }
+
+ /**
+ * Called to initialize the data in the <code>VertexBuffer</code>. Must only
+ * be called once.
+ *
+ * @param usage The usage for the data, or how often will the data
+ * be updated per frame. See the {@link Usage} enum.
+ * @param components The number of components per element.
+ * @param format The {@link Format format}, or data-type of a single
+ * component.
+ * @param data A native buffer, the format of which matches the {@link Format}
+ * argument.
+ */
+ public void setupData(Usage usage, int components, Format format, Buffer data){
+ if (id != -1)
+ throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
+
+ if (usage == null || format == null || data == null)
+ throw new IllegalArgumentException("None of the arguments can be null");
+
+ if (data.isReadOnly())
+ throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
+
+ if (components < 1 || components > 4)
+ throw new IllegalArgumentException("components must be between 1 and 4");
+
+ this.data = data;
+ this.components = components;
+ this.usage = usage;
+ this.format = format;
+ this.componentsLength = components * format.getComponentSize();
+ this.lastLimit = data.limit();
+ setUpdateNeeded();
+ }
+
+ /**
+ * Called to update the data in the buffer with new data. Can only
+ * be called after {@link VertexBuffer#setupData(com.jme3.scene.VertexBuffer.Usage, int, com.jme3.scene.VertexBuffer.Format, java.nio.Buffer) }
+ * has been called. Note that it is fine to call this method on the
+ * data already set, e.g. vb.updateData(vb.getData()), this will just
+ * set the proper update flag indicating the data should be sent to the GPU
+ * again.
+ * It is allowed to specify a buffer with different capacity than the
+ * originally set buffer.
+ *
+ * @param data The data buffer to set
+ */
+ public void updateData(Buffer data){
+ if (id != -1){
+ // request to update data is okay
+ }
+
+ // Check if the data buffer is read-only which is a sign
+ // of a bug on the part of the caller
+ if (data != null && data.isReadOnly()) {
+ throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
+ }
+
+ // will force renderer to call glBufferData again
+ if (data != null && (this.data.getClass() != data.getClass() || data.limit() != lastLimit)){
+ dataSizeChanged = true;
+ lastLimit = data.limit();
+ }
+
+ this.data = data;
+ setUpdateNeeded();
+ }
+
+ /**
+ * Returns true if the data size of the VertexBuffer has changed.
+ * Internal use only.
+ * @return true if the data size has changed
+ */
+ public boolean hasDataSizeChanged() {
+ return dataSizeChanged;
+ }
+
+ @Override
+ public void clearUpdateNeeded(){
+ super.clearUpdateNeeded();
+ dataSizeChanged = false;
+ }
+
+ /**
+ * Converts single floating-point data to {@link Format#Half half} floating-point data.
+ */
+ public void convertToHalf(){
+ if (id != -1)
+ throw new UnsupportedOperationException("Data has already been sent.");
+
+ if (format != Format.Float)
+ throw new IllegalStateException("Format must be float!");
+
+ int numElements = data.capacity() / components;
+ format = Format.Half;
+ this.componentsLength = components * format.getComponentSize();
+
+ ByteBuffer halfData = BufferUtils.createByteBuffer(componentsLength * numElements);
+ halfData.rewind();
+
+ FloatBuffer floatData = (FloatBuffer) data;
+ floatData.rewind();
+
+ for (int i = 0; i < floatData.capacity(); i++){
+ float f = floatData.get(i);
+ short half = FastMath.convertFloatToHalf(f);
+ halfData.putShort(half);
+ }
+ this.data = halfData;
+ setUpdateNeeded();
+ dataSizeChanged = true;
+ }
+
+ /**
+ * Reduces the capacity of the buffer to the given amount
+ * of elements, any elements at the end of the buffer are truncated
+ * as necessary.
+ *
+ * @param numElements The number of elements to reduce to.
+ */
+ public void compact(int numElements){
+ int total = components * numElements;
+ data.clear();
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bbuf = (ByteBuffer) data;
+ bbuf.limit(total);
+ ByteBuffer bnewBuf = BufferUtils.createByteBuffer(total);
+ bnewBuf.put(bbuf);
+ data = bnewBuf;
+ break;
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sbuf = (ShortBuffer) data;
+ sbuf.limit(total);
+ ShortBuffer snewBuf = BufferUtils.createShortBuffer(total);
+ snewBuf.put(sbuf);
+ data = snewBuf;
+ break;
+ case Int:
+ case UnsignedInt:
+ IntBuffer ibuf = (IntBuffer) data;
+ ibuf.limit(total);
+ IntBuffer inewBuf = BufferUtils.createIntBuffer(total);
+ inewBuf.put(ibuf);
+ data = inewBuf;
+ break;
+ case Float:
+ FloatBuffer fbuf = (FloatBuffer) data;
+ fbuf.limit(total);
+ FloatBuffer fnewBuf = BufferUtils.createFloatBuffer(total);
+ fnewBuf.put(fbuf);
+ data = fnewBuf;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+ data.clear();
+ setUpdateNeeded();
+ dataSizeChanged = true;
+ }
+
+ /**
+ * Modify a component inside an element.
+ * The <code>val</code> parameter must be in the buffer's format:
+ * {@link Format}.
+ *
+ * @param elementIndex The element index to modify
+ * @param componentIndex The component index to modify
+ * @param val The value to set, either byte, short, int or float depending
+ * on the {@link Format}.
+ */
+ public void setElementComponent(int elementIndex, int componentIndex, Object val){
+ int inPos = elementIndex * components;
+ int elementPos = componentIndex;
+
+ if (format == Format.Half){
+ inPos *= 2;
+ elementPos *= 2;
+ }
+
+ data.clear();
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bin = (ByteBuffer) data;
+ bin.put(inPos + elementPos, (Byte)val);
+ break;
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sin = (ShortBuffer) data;
+ sin.put(inPos + elementPos, (Short)val);
+ break;
+ case Int:
+ case UnsignedInt:
+ IntBuffer iin = (IntBuffer) data;
+ iin.put(inPos + elementPos, (Integer)val);
+ break;
+ case Float:
+ FloatBuffer fin = (FloatBuffer) data;
+ fin.put(inPos + elementPos, (Float)val);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+ }
+
+ /**
+ * Get the component inside an element.
+ *
+ * @param elementIndex The element index
+ * @param componentIndex The component index
+ * @return The component, as one of the primitive types, byte, short,
+ * int or float.
+ */
+ public Object getElementComponent(int elementIndex, int componentIndex){
+ int inPos = elementIndex * components;
+ int elementPos = componentIndex;
+
+ if (format == Format.Half){
+ inPos *= 2;
+ elementPos *= 2;
+ }
+
+ Buffer srcData = getDataReadOnly();
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bin = (ByteBuffer) srcData;
+ return bin.get(inPos + elementPos);
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sin = (ShortBuffer) srcData;
+ return sin.get(inPos + elementPos);
+ case Int:
+ case UnsignedInt:
+ IntBuffer iin = (IntBuffer) srcData;
+ return iin.get(inPos + elementPos);
+ case Float:
+ FloatBuffer fin = (FloatBuffer) srcData;
+ return fin.get(inPos + elementPos);
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+ }
+
+ /**
+ * Copies a single element of data from this <code>VertexBuffer</code>
+ * to the given output VertexBuffer.
+ *
+ * @param inIndex The input element index
+ * @param outVb The buffer to copy to
+ * @param outIndex The output element index
+ *
+ * @throws IllegalArgumentException If the formats of the buffers do not
+ * match.
+ */
+ public void copyElement(int inIndex, VertexBuffer outVb, int outIndex){
+ copyElements(inIndex, outVb, outIndex, 1);
+ }
+
+ /**
+ * Copies a sequence of elements of data from this <code>VertexBuffer</code>
+ * to the given output VertexBuffer.
+ *
+ * @param inIndex The input element index
+ * @param outVb The buffer to copy to
+ * @param outIndex The output element index
+ * @param len The number of elements to copy
+ *
+ * @throws IllegalArgumentException If the formats of the buffers do not
+ * match.
+ */
+ public void copyElements(int inIndex, VertexBuffer outVb, int outIndex, int len){
+ if (outVb.format != format || outVb.components != components)
+ throw new IllegalArgumentException("Buffer format mismatch. Cannot copy");
+
+ int inPos = inIndex * components;
+ int outPos = outIndex * components;
+ int elementSz = components;
+ if (format == Format.Half){
+ // because half is stored as bytebuf but its 2 bytes long
+ inPos *= 2;
+ outPos *= 2;
+ elementSz *= 2;
+ }
+
+ // Make sure to grab a read-only copy in case some other
+ // thread is also accessing the buffer and messing with its
+ // position()
+ Buffer srcData = getDataReadOnly();
+ outVb.data.clear();
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ case Half:
+ ByteBuffer bin = (ByteBuffer) srcData;
+ ByteBuffer bout = (ByteBuffer) outVb.data;
+ bin.position(inPos).limit(inPos + elementSz * len);
+ bout.position(outPos).limit(outPos + elementSz * len);
+ bout.put(bin);
+ break;
+ case Short:
+ case UnsignedShort:
+ ShortBuffer sin = (ShortBuffer) srcData;
+ ShortBuffer sout = (ShortBuffer) outVb.data;
+ sin.position(inPos).limit(inPos + elementSz * len);
+ sout.position(outPos).limit(outPos + elementSz * len);
+ sout.put(sin);
+ break;
+ case Int:
+ case UnsignedInt:
+ IntBuffer iin = (IntBuffer) srcData;
+ IntBuffer iout = (IntBuffer) outVb.data;
+ iin.position(inPos).limit(inPos + elementSz * len);
+ iout.position(outPos).limit(outPos + elementSz * len);
+ iout.put(iin);
+ break;
+ case Float:
+ FloatBuffer fin = (FloatBuffer) srcData;
+ FloatBuffer fout = (FloatBuffer) outVb.data;
+ fin.position(inPos).limit(inPos + elementSz * len);
+ fout.position(outPos).limit(outPos + elementSz * len);
+ fout.put(fin);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
+ }
+
+ // Clear the output buffer to rewind it and reset its
+ // limit from where we shortened it above.
+ outVb.data.clear();
+ }
+
+ /**
+ * Creates a {@link Buffer} that satisfies the given type and size requirements
+ * of the parameters. The buffer will be of the type specified by
+ * {@link Format format} and would be able to contain the given number
+ * of elements with the given number of components in each element.
+ */
+ public static Buffer createBuffer(Format format, int components, int numElements){
+ if (components < 1 || components > 4)
+ throw new IllegalArgumentException("Num components must be between 1 and 4");
+
+ int total = numElements * components;
+
+ switch (format){
+ case Byte:
+ case UnsignedByte:
+ return BufferUtils.createByteBuffer(total);
+ case Half:
+ return BufferUtils.createByteBuffer(total * 2);
+ case Short:
+ case UnsignedShort:
+ return BufferUtils.createShortBuffer(total);
+ case Int:
+ case UnsignedInt:
+ return BufferUtils.createIntBuffer(total);
+ case Float:
+ return BufferUtils.createFloatBuffer(total);
+ case Double:
+ return BufferUtils.createDoubleBuffer(total);
+ default:
+ throw new UnsupportedOperationException("Unrecoginized buffer format: "+format);
+ }
+ }
+
+ /**
+ * Creates a deep clone of the {@link VertexBuffer}.
+ *
+ * @return Deep clone of this buffer
+ */
+ @Override
+ public VertexBuffer clone(){
+ // NOTE: Superclass GLObject automatically creates shallow clone
+ // e.g re-use ID.
+ VertexBuffer vb = (VertexBuffer) super.clone();
+ vb.handleRef = new Object();
+ vb.id = -1;
+ if (data != null) {
+ // Make sure to pass a read-only buffer to clone so that
+ // the position information doesn't get clobbered by another
+ // reading thread during cloning (and vice versa) since this is
+ // a purely read-only operation.
+ vb.updateData(BufferUtils.clone(getDataReadOnly()));
+ }
+
+ return vb;
+ }
+
+ /**
+ * Creates a deep clone of this VertexBuffer but overrides the
+ * {@link Type}.
+ *
+ * @param overrideType The type of the cloned VertexBuffer
+ * @return A deep clone of the buffer
+ */
+ public VertexBuffer clone(Type overrideType){
+ VertexBuffer vb = new VertexBuffer(overrideType);
+ vb.components = components;
+ vb.componentsLength = componentsLength;
+
+ // Make sure to pass a read-only buffer to clone so that
+ // the position information doesn't get clobbered by another
+ // reading thread during cloning (and vice versa) since this is
+ // a purely read-only operation.
+ vb.data = BufferUtils.clone(getDataReadOnly());
+ vb.format = format;
+ vb.handleRef = new Object();
+ vb.id = -1;
+ vb.normalized = normalized;
+ vb.offset = offset;
+ vb.stride = stride;
+ vb.updateNeeded = true;
+ vb.usage = usage;
+ return vb;
+ }
+
+ @Override
+ public String toString(){
+ String dataTxt = null;
+ if (data != null){
+ dataTxt = ", elements="+data.capacity();
+ }
+ return getClass().getSimpleName() + "[fmt="+format.name()
+ +", type="+bufType.name()
+ +", usage="+usage.name()
+ +dataTxt+"]";
+ }
+
+ @Override
+ public void resetObject() {
+// assert this.id != -1;
+ this.id = -1;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((Renderer)rendererObject).deleteBuffer(this);
+ }
+
+ @Override
+ public NativeObject createDestructableClone(){
+ return new VertexBuffer(id);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(components, "components", 0);
+ oc.write(usage, "usage", Usage.Dynamic);
+ oc.write(bufType, "buffer_type", null);
+ oc.write(format, "format", Format.Float);
+ oc.write(normalized, "normalized", false);
+ oc.write(offset, "offset", 0);
+ oc.write(stride, "stride", 0);
+
+ String dataName = "data" + format.name();
+ Buffer roData = getDataReadOnly();
+ switch (format){
+ case Float:
+ oc.write((FloatBuffer) roData, dataName, null);
+ break;
+ case Short:
+ case UnsignedShort:
+ oc.write((ShortBuffer) roData, dataName, null);
+ break;
+ case UnsignedByte:
+ case Byte:
+ case Half:
+ oc.write((ByteBuffer) roData, dataName, null);
+ break;
+ case Int:
+ case UnsignedInt:
+ oc.write((IntBuffer) roData, dataName, null);
+ break;
+ default:
+ throw new IOException("Unsupported export buffer format: "+format);
+ }
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ components = ic.readInt("components", 0);
+ usage = ic.readEnum("usage", Usage.class, Usage.Dynamic);
+ bufType = ic.readEnum("buffer_type", Type.class, null);
+ format = ic.readEnum("format", Format.class, Format.Float);
+ normalized = ic.readBoolean("normalized", false);
+ offset = ic.readInt("offset", 0);
+ stride = ic.readInt("stride", 0);
+ componentsLength = components * format.getComponentSize();
+
+ String dataName = "data" + format.name();
+ switch (format){
+ case Float:
+ data = ic.readFloatBuffer(dataName, null);
+ break;
+ case Short:
+ case UnsignedShort:
+ data = ic.readShortBuffer(dataName, null);
+ break;
+ case UnsignedByte:
+ case Byte:
+ case Half:
+ data = ic.readByteBuffer(dataName, null);
+ break;
+ case Int:
+ case UnsignedInt:
+ data = ic.readIntBuffer(dataName, null);
+ break;
+ default:
+ throw new IOException("Unsupported import buffer format: "+format);
+ }
+ }
+
+}