Home | History | Annotate | Download | only in scene
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.jme3.scene;
     34 
     35 import com.jme3.export.*;
     36 import com.jme3.math.FastMath;
     37 import com.jme3.renderer.Renderer;
     38 import com.jme3.util.BufferUtils;
     39 import com.jme3.util.NativeObject;
     40 import java.io.IOException;
     41 import java.nio.*;
     42 
     43 /**
     44  * A <code>VertexBuffer</code> contains a particular type of geometry
     45  * data used by {@link Mesh}es. Every VertexBuffer set on a <code>Mesh</code>
     46  * is sent as an attribute to the vertex shader to be processed.
     47  * <p>
     48  * Several terms are used throughout the javadoc for this class, explanation:
     49  * <ul>
     50  * <li>Element - A single element is the largest individual object
     51  * inside a VertexBuffer. E.g. if the VertexBuffer is used to store 3D position
     52  * data, then an element will be a single 3D vector.</li>
     53  * <li>Component - A component represents the parts inside an element.
     54  * For a 3D vector, a single component is one of the dimensions, X, Y or Z.</li>
     55  * </ul>
     56  */
     57 public class VertexBuffer extends NativeObject implements Savable, Cloneable {
     58 
     59     /**
     60      * Type of buffer. Specifies the actual attribute it defines.
     61      */
     62     public static enum Type {
     63         /**
     64          * Position of the vertex (3 floats)
     65          */
     66         Position,
     67 
     68         /**
     69          * The size of the point when using point buffers (float).
     70          */
     71         Size,
     72 
     73         /**
     74          * Normal vector, normalized (3 floats).
     75          */
     76         Normal,
     77 
     78         /**
     79          * Texture coordinate (2 float)
     80          */
     81         TexCoord,
     82 
     83         /**
     84          * Color and Alpha (4 floats)
     85          */
     86         Color,
     87 
     88         /**
     89          * Tangent vector, normalized (4 floats) (x,y,z,w)
     90          * the w component is called the binormal parity, is not normalized and is either 1f or -1f
     91          * It's used to compuste the direction on the binormal verctor on the GPU at render time.
     92          */
     93         Tangent,
     94 
     95         /**
     96          * Binormal vector, normalized (3 floats, optional)
     97          */
     98         Binormal,
     99 
    100         /**
    101          * Specifies the source data for various vertex buffers
    102          * when interleaving is used. By default the format is
    103          * byte.
    104          */
    105         InterleavedData,
    106 
    107         /**
    108          * Do not use.
    109          */
    110         @Deprecated
    111         MiscAttrib,
    112 
    113         /**
    114          * Specifies the index buffer, must contain integer data
    115          * (ubyte, ushort, or uint).
    116          */
    117         Index,
    118 
    119         /**
    120          * Initial vertex position, used with animation.
    121          * Should have the same format and size as {@link Type#Position}.
    122          * If used with software skinning, the usage should be
    123          * {@link Usage#CpuOnly}, and the buffer should be allocated
    124          * on the heap.
    125          */
    126         BindPosePosition,
    127 
    128         /**
    129          * Initial vertex normals, used with animation.
    130          * Should have the same format and size as {@link Type#Normal}.
    131          * If used with software skinning, the usage should be
    132          * {@link Usage#CpuOnly}, and the buffer should be allocated
    133          * on the heap.
    134          */
    135         BindPoseNormal,
    136 
    137         /**
    138          * Bone weights, used with animation (4 floats).
    139          * If used with software skinning, the usage should be
    140          * {@link Usage#CpuOnly}, and the buffer should be allocated
    141          * on the heap.
    142          */
    143         BoneWeight,
    144 
    145         /**
    146          * Bone indices, used with animation (4 ubytes).
    147          * If used with software skinning, the usage should be
    148          * {@link Usage#CpuOnly}, and the buffer should be allocated
    149          * on the heap.
    150          */
    151         BoneIndex,
    152 
    153         /**
    154          * Texture coordinate #2
    155          */
    156         TexCoord2,
    157 
    158         /**
    159          * Texture coordinate #3
    160          */
    161         TexCoord3,
    162 
    163         /**
    164          * Texture coordinate #4
    165          */
    166         TexCoord4,
    167 
    168         /**
    169          * Texture coordinate #5
    170          */
    171         TexCoord5,
    172 
    173         /**
    174          * Texture coordinate #6
    175          */
    176         TexCoord6,
    177 
    178         /**
    179          * Texture coordinate #7
    180          */
    181         TexCoord7,
    182 
    183         /**
    184          * Texture coordinate #8
    185          */
    186         TexCoord8,
    187 
    188         /**
    189          * Initial vertex tangents, used with animation.
    190          * Should have the same format and size as {@link Type#Tangent}.
    191          * If used with software skinning, the usage should be
    192          * {@link Usage#CpuOnly}, and the buffer should be allocated
    193          * on the heap.
    194          */
    195         BindPoseTangent,
    196     }
    197 
    198     /**
    199      * The usage of the VertexBuffer, specifies how often the buffer
    200      * is used. This can determine if a vertex buffer is placed in VRAM
    201      * or held in video memory, but no guarantees are made- it's only a hint.
    202      */
    203     public static enum Usage {
    204 
    205         /**
    206          * Mesh data is sent once and very rarely updated.
    207          */
    208         Static,
    209 
    210         /**
    211          * Mesh data is updated occasionally (once per frame or less).
    212          */
    213         Dynamic,
    214 
    215         /**
    216          * Mesh data is updated every frame.
    217          */
    218         Stream,
    219 
    220         /**
    221          * Mesh data is <em>not</em> sent to GPU at all. It is only
    222          * used by the CPU.
    223          */
    224         CpuOnly;
    225     }
    226 
    227     /**
    228      * Specifies format of the data stored in the buffer.
    229      * This should directly correspond to the buffer's class, for example,
    230      * an {@link Format#UnsignedShort} formatted buffer should use the
    231      * class {@link ShortBuffer} (e.g. the closest resembling type).
    232      * For the {@link Format#Half} type, {@link ByteBuffer}s should
    233      * be used.
    234      */
    235     public static enum Format {
    236         /**
    237          * Half precision floating point.
    238          * 2 bytes, signed.
    239          */
    240         Half(2),
    241 
    242         /**
    243          * Single precision floating point.
    244          * 4 bytes, signed
    245          */
    246         Float(4),
    247 
    248         /**
    249          * Double precision floating point.
    250          * 8 bytes, signed. May not
    251          * be supported by all GPUs.
    252          */
    253         Double(8),
    254 
    255         /**
    256          * 1 byte integer, signed.
    257          */
    258         Byte(1),
    259 
    260         /**
    261          * 1 byte integer, unsigned.
    262          */
    263         UnsignedByte(1),
    264 
    265         /**
    266          * 2 byte integer, signed.
    267          */
    268         Short(2),
    269 
    270         /**
    271          * 2 byte integer, unsigned.
    272          */
    273         UnsignedShort(2),
    274 
    275         /**
    276          * 4 byte integer, signed.
    277          */
    278         Int(4),
    279 
    280         /**
    281          * 4 byte integer, unsigned.
    282          */
    283         UnsignedInt(4);
    284 
    285         private int componentSize = 0;
    286 
    287         Format(int componentSize){
    288             this.componentSize = componentSize;
    289         }
    290 
    291         /**
    292          * Returns the size in bytes of this data type.
    293          *
    294          * @return Size in bytes of this data type.
    295          */
    296         public int getComponentSize(){
    297             return componentSize;
    298         }
    299     }
    300 
    301     protected int offset = 0;
    302     protected int lastLimit = 0;
    303     protected int stride = 0;
    304     protected int components = 0;
    305 
    306     /**
    307      * derived from components * format.getComponentSize()
    308      */
    309     protected transient int componentsLength = 0;
    310     protected Buffer data = null;
    311     protected Usage usage;
    312     protected Type bufType;
    313     protected Format format;
    314     protected boolean normalized = false;
    315     protected transient boolean dataSizeChanged = false;
    316 
    317     /**
    318      * Creates an empty, uninitialized buffer.
    319      * Must call setupData() to initialize.
    320      */
    321     public VertexBuffer(Type type){
    322         super(VertexBuffer.class);
    323         this.bufType = type;
    324     }
    325 
    326     /**
    327      * Serialization only. Do not use.
    328      */
    329     public VertexBuffer(){
    330         super(VertexBuffer.class);
    331     }
    332 
    333     protected VertexBuffer(int id){
    334         super(VertexBuffer.class, id);
    335     }
    336 
    337     /**
    338      * @return The offset after which the data is sent to the GPU.
    339      *
    340      * @see #setOffset(int)
    341      */
    342     public int getOffset() {
    343         return offset;
    344     }
    345 
    346     /**
    347      * @param offset Specify the offset (in bytes) from the start of the buffer
    348      * after which the data is sent to the GPU.
    349      */
    350     public void setOffset(int offset) {
    351         this.offset = offset;
    352     }
    353 
    354     /**
    355      * @return The stride (in bytes) for the data.
    356      *
    357      * @see #setStride(int)
    358      */
    359     public int getStride() {
    360         return stride;
    361     }
    362 
    363     /**
    364      * Set the stride (in bytes) for the data.
    365      * <p>
    366      * If the data is packed in the buffer, then stride is 0, if there's other
    367      * data that is between the current component and the next component in the
    368      * buffer, then this specifies the size in bytes of that additional data.
    369      *
    370      * @param stride the stride (in bytes) for the data
    371      */
    372     public void setStride(int stride) {
    373         this.stride = stride;
    374     }
    375 
    376     /**
    377      * Returns the raw internal data buffer used by this VertexBuffer.
    378      * This buffer is not safe to call from multiple threads since buffers
    379      * have their own internal position state that cannot be shared.
    380      * Call getData().duplicate(), getData().asReadOnlyBuffer(), or
    381      * the more convenient getDataReadOnly() if the buffer may be accessed
    382      * from multiple threads.
    383      *
    384      * @return A native buffer, in the specified {@link Format format}.
    385      */
    386     public Buffer getData(){
    387         return data;
    388     }
    389 
    390     /**
    391      * Returns a safe read-only version of this VertexBuffer's data.  The
    392      * contents of the buffer will reflect whatever changes are made on
    393      * other threads (eventually) but these should not be used in that way.
    394      * This method provides a read-only buffer that is safe to _read_ from
    395      * a separate thread since it has its own book-keeping state (position, limit, etc.)
    396      *
    397      * @return A rewound native buffer in the specified {@link Format format}
    398      *         that is safe to read from a separate thread from other readers.
    399      */
    400     public Buffer getDataReadOnly() {
    401 
    402         if (data == null) {
    403             return null;
    404         }
    405 
    406         // Create a read-only duplicate().  Note: this does not copy
    407         // the underlying memory, it just creates a new read-only wrapper
    408         // with its own buffer position state.
    409 
    410         // Unfortunately, this is not 100% straight forward since Buffer
    411         // does not have an asReadOnlyBuffer() method.
    412         Buffer result;
    413         if( data instanceof ByteBuffer ) {
    414             result = ((ByteBuffer)data).asReadOnlyBuffer();
    415         } else if( data instanceof FloatBuffer ) {
    416             result = ((FloatBuffer)data).asReadOnlyBuffer();
    417         } else if( data instanceof ShortBuffer ) {
    418             result = ((ShortBuffer)data).asReadOnlyBuffer();
    419         } else if( data instanceof IntBuffer ) {
    420             result = ((IntBuffer)data).asReadOnlyBuffer();
    421         } else {
    422             throw new UnsupportedOperationException( "Cannot get read-only view of buffer type:" + data );
    423         }
    424 
    425         // Make sure the caller gets a consistent view since we may
    426         // have grabbed this buffer while another thread was reading
    427         // the raw data.
    428         result.rewind();
    429 
    430         return result;
    431     }
    432 
    433     /**
    434      * @return The usage of this buffer. See {@link Usage} for more
    435      * information.
    436      */
    437     public Usage getUsage(){
    438         return usage;
    439     }
    440 
    441     /**
    442      * @param usage The usage of this buffer. See {@link Usage} for more
    443      * information.
    444      */
    445     public void setUsage(Usage usage){
    446 //        if (id != -1)
    447 //            throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
    448 
    449         this.usage = usage;
    450     }
    451 
    452     /**
    453      * @param normalized Set to true if integer components should be converted
    454      * from their maximal range into the range 0.0 - 1.0 when converted to
    455      * a floating-point value for the shader.
    456      * E.g. if the {@link Format} is {@link Format#UnsignedInt}, then
    457      * the components will be converted to the range 0.0 - 1.0 by dividing
    458      * every integer by 2^32.
    459      */
    460     public void setNormalized(boolean normalized){
    461         this.normalized = normalized;
    462     }
    463 
    464     /**
    465      * @return True if integer components should be converted to the range 0-1.
    466      * @see VertexBuffer#setNormalized(boolean)
    467      */
    468     public boolean isNormalized(){
    469         return normalized;
    470     }
    471 
    472     /**
    473      * @return The type of information that this buffer has.
    474      */
    475     public Type getBufferType(){
    476         return bufType;
    477     }
    478 
    479     /**
    480      * @return The {@link Format format}, or data type of the data.
    481      */
    482     public Format getFormat(){
    483         return format;
    484     }
    485 
    486     /**
    487      * @return The number of components of the given {@link Format format} per
    488      * element.
    489      */
    490     public int getNumComponents(){
    491         return components;
    492     }
    493 
    494     /**
    495      * @return The total number of data elements in the data buffer.
    496      */
    497     public int getNumElements(){
    498         int elements = data.capacity() / components;
    499         if (format == Format.Half)
    500             elements /= 2;
    501         return elements;
    502     }
    503 
    504     /**
    505      * Called to initialize the data in the <code>VertexBuffer</code>. Must only
    506      * be called once.
    507      *
    508      * @param usage The usage for the data, or how often will the data
    509      * be updated per frame. See the {@link Usage} enum.
    510      * @param components The number of components per element.
    511      * @param format The {@link Format format}, or data-type of a single
    512      * component.
    513      * @param data A native buffer, the format of which matches the {@link Format}
    514      * argument.
    515      */
    516     public void setupData(Usage usage, int components, Format format, Buffer data){
    517         if (id != -1)
    518             throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
    519 
    520         if (usage == null || format == null || data == null)
    521             throw new IllegalArgumentException("None of the arguments can be null");
    522 
    523         if (data.isReadOnly())
    524             throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
    525 
    526         if (components < 1 || components > 4)
    527             throw new IllegalArgumentException("components must be between 1 and 4");
    528 
    529         this.data = data;
    530         this.components = components;
    531         this.usage = usage;
    532         this.format = format;
    533         this.componentsLength = components * format.getComponentSize();
    534         this.lastLimit = data.limit();
    535         setUpdateNeeded();
    536     }
    537 
    538     /**
    539      * Called to update the data in the buffer with new data. Can only
    540      * be called after {@link VertexBuffer#setupData(com.jme3.scene.VertexBuffer.Usage, int, com.jme3.scene.VertexBuffer.Format, java.nio.Buffer) }
    541      * has been called. Note that it is fine to call this method on the
    542      * data already set, e.g. vb.updateData(vb.getData()), this will just
    543      * set the proper update flag indicating the data should be sent to the GPU
    544      * again.
    545      * It is allowed to specify a buffer with different capacity than the
    546      * originally set buffer.
    547      *
    548      * @param data The data buffer to set
    549      */
    550     public void updateData(Buffer data){
    551         if (id != -1){
    552             // request to update data is okay
    553         }
    554 
    555         // Check if the data buffer is read-only which is a sign
    556         // of a bug on the part of the caller
    557         if (data != null && data.isReadOnly()) {
    558             throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
    559         }
    560 
    561         // will force renderer to call glBufferData again
    562         if (data != null && (this.data.getClass() != data.getClass() || data.limit() != lastLimit)){
    563             dataSizeChanged = true;
    564             lastLimit = data.limit();
    565         }
    566 
    567         this.data = data;
    568         setUpdateNeeded();
    569     }
    570 
    571     /**
    572      * Returns true if the data size of the VertexBuffer has changed.
    573      * Internal use only.
    574      * @return true if the data size has changed
    575      */
    576     public boolean hasDataSizeChanged() {
    577         return dataSizeChanged;
    578     }
    579 
    580     @Override
    581     public void clearUpdateNeeded(){
    582         super.clearUpdateNeeded();
    583         dataSizeChanged = false;
    584     }
    585 
    586     /**
    587      * Converts single floating-point data to {@link Format#Half half} floating-point data.
    588      */
    589     public void convertToHalf(){
    590         if (id != -1)
    591             throw new UnsupportedOperationException("Data has already been sent.");
    592 
    593         if (format != Format.Float)
    594             throw new IllegalStateException("Format must be float!");
    595 
    596         int numElements = data.capacity() / components;
    597         format = Format.Half;
    598         this.componentsLength = components * format.getComponentSize();
    599 
    600         ByteBuffer halfData = BufferUtils.createByteBuffer(componentsLength * numElements);
    601         halfData.rewind();
    602 
    603         FloatBuffer floatData = (FloatBuffer) data;
    604         floatData.rewind();
    605 
    606         for (int i = 0; i < floatData.capacity(); i++){
    607             float f = floatData.get(i);
    608             short half = FastMath.convertFloatToHalf(f);
    609             halfData.putShort(half);
    610         }
    611         this.data = halfData;
    612         setUpdateNeeded();
    613         dataSizeChanged = true;
    614     }
    615 
    616     /**
    617      * Reduces the capacity of the buffer to the given amount
    618      * of elements, any elements at the end of the buffer are truncated
    619      * as necessary.
    620      *
    621      * @param numElements The number of elements to reduce to.
    622      */
    623     public void compact(int numElements){
    624         int total = components * numElements;
    625         data.clear();
    626         switch (format){
    627             case Byte:
    628             case UnsignedByte:
    629             case Half:
    630                 ByteBuffer bbuf = (ByteBuffer) data;
    631                 bbuf.limit(total);
    632                 ByteBuffer bnewBuf = BufferUtils.createByteBuffer(total);
    633                 bnewBuf.put(bbuf);
    634                 data = bnewBuf;
    635                 break;
    636             case Short:
    637             case UnsignedShort:
    638                 ShortBuffer sbuf = (ShortBuffer) data;
    639                 sbuf.limit(total);
    640                 ShortBuffer snewBuf = BufferUtils.createShortBuffer(total);
    641                 snewBuf.put(sbuf);
    642                 data = snewBuf;
    643                 break;
    644             case Int:
    645             case UnsignedInt:
    646                 IntBuffer ibuf = (IntBuffer) data;
    647                 ibuf.limit(total);
    648                 IntBuffer inewBuf = BufferUtils.createIntBuffer(total);
    649                 inewBuf.put(ibuf);
    650                 data = inewBuf;
    651                 break;
    652             case Float:
    653                 FloatBuffer fbuf = (FloatBuffer) data;
    654                 fbuf.limit(total);
    655                 FloatBuffer fnewBuf = BufferUtils.createFloatBuffer(total);
    656                 fnewBuf.put(fbuf);
    657                 data = fnewBuf;
    658                 break;
    659             default:
    660                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
    661         }
    662         data.clear();
    663         setUpdateNeeded();
    664         dataSizeChanged = true;
    665     }
    666 
    667     /**
    668      * Modify a component inside an element.
    669      * The <code>val</code> parameter must be in the buffer's format:
    670      * {@link Format}.
    671      *
    672      * @param elementIndex The element index to modify
    673      * @param componentIndex The component index to modify
    674      * @param val The value to set, either byte, short, int or float depending
    675      * on the {@link Format}.
    676      */
    677     public void setElementComponent(int elementIndex, int componentIndex, Object val){
    678         int inPos = elementIndex * components;
    679         int elementPos = componentIndex;
    680 
    681         if (format == Format.Half){
    682             inPos *= 2;
    683             elementPos *= 2;
    684         }
    685 
    686         data.clear();
    687 
    688         switch (format){
    689             case Byte:
    690             case UnsignedByte:
    691             case Half:
    692                 ByteBuffer bin = (ByteBuffer) data;
    693                 bin.put(inPos + elementPos, (Byte)val);
    694                 break;
    695             case Short:
    696             case UnsignedShort:
    697                 ShortBuffer sin = (ShortBuffer) data;
    698                 sin.put(inPos + elementPos, (Short)val);
    699                 break;
    700             case Int:
    701             case UnsignedInt:
    702                 IntBuffer iin = (IntBuffer) data;
    703                 iin.put(inPos + elementPos, (Integer)val);
    704                 break;
    705             case Float:
    706                 FloatBuffer fin = (FloatBuffer) data;
    707                 fin.put(inPos + elementPos, (Float)val);
    708                 break;
    709             default:
    710                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
    711         }
    712     }
    713 
    714     /**
    715      * Get the component inside an element.
    716      *
    717      * @param elementIndex The element index
    718      * @param componentIndex The component index
    719      * @return The component, as one of the primitive types, byte, short,
    720      * int or float.
    721      */
    722     public Object getElementComponent(int elementIndex, int componentIndex){
    723         int inPos = elementIndex * components;
    724         int elementPos = componentIndex;
    725 
    726         if (format == Format.Half){
    727             inPos *= 2;
    728             elementPos *= 2;
    729         }
    730 
    731         Buffer srcData = getDataReadOnly();
    732 
    733         switch (format){
    734             case Byte:
    735             case UnsignedByte:
    736             case Half:
    737                 ByteBuffer bin = (ByteBuffer) srcData;
    738                 return bin.get(inPos + elementPos);
    739             case Short:
    740             case UnsignedShort:
    741                 ShortBuffer sin = (ShortBuffer) srcData;
    742                 return sin.get(inPos + elementPos);
    743             case Int:
    744             case UnsignedInt:
    745                 IntBuffer iin = (IntBuffer) srcData;
    746                 return iin.get(inPos + elementPos);
    747             case Float:
    748                 FloatBuffer fin = (FloatBuffer) srcData;
    749                 return fin.get(inPos + elementPos);
    750             default:
    751                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
    752         }
    753     }
    754 
    755     /**
    756      * Copies a single element of data from this <code>VertexBuffer</code>
    757      * to the given output VertexBuffer.
    758      *
    759      * @param inIndex The input element index
    760      * @param outVb The buffer to copy to
    761      * @param outIndex The output element index
    762      *
    763      * @throws IllegalArgumentException If the formats of the buffers do not
    764      * match.
    765      */
    766     public void copyElement(int inIndex, VertexBuffer outVb, int outIndex){
    767         copyElements(inIndex, outVb, outIndex, 1);
    768     }
    769 
    770     /**
    771      * Copies a sequence of elements of data from this <code>VertexBuffer</code>
    772      * to the given output VertexBuffer.
    773      *
    774      * @param inIndex The input element index
    775      * @param outVb The buffer to copy to
    776      * @param outIndex The output element index
    777      * @param len The number of elements to copy
    778      *
    779      * @throws IllegalArgumentException If the formats of the buffers do not
    780      * match.
    781      */
    782     public void copyElements(int inIndex, VertexBuffer outVb, int outIndex, int len){
    783         if (outVb.format != format || outVb.components != components)
    784             throw new IllegalArgumentException("Buffer format mismatch. Cannot copy");
    785 
    786         int inPos  = inIndex  * components;
    787         int outPos = outIndex * components;
    788         int elementSz = components;
    789         if (format == Format.Half){
    790             // because half is stored as bytebuf but its 2 bytes long
    791             inPos *= 2;
    792             outPos *= 2;
    793             elementSz *= 2;
    794         }
    795 
    796         // Make sure to grab a read-only copy in case some other
    797         // thread is also accessing the buffer and messing with its
    798         // position()
    799         Buffer srcData = getDataReadOnly();
    800         outVb.data.clear();
    801 
    802         switch (format){
    803             case Byte:
    804             case UnsignedByte:
    805             case Half:
    806                 ByteBuffer bin = (ByteBuffer) srcData;
    807                 ByteBuffer bout = (ByteBuffer) outVb.data;
    808                 bin.position(inPos).limit(inPos + elementSz * len);
    809                 bout.position(outPos).limit(outPos + elementSz * len);
    810                 bout.put(bin);
    811                 break;
    812             case Short:
    813             case UnsignedShort:
    814                 ShortBuffer sin = (ShortBuffer) srcData;
    815                 ShortBuffer sout = (ShortBuffer) outVb.data;
    816                 sin.position(inPos).limit(inPos + elementSz * len);
    817                 sout.position(outPos).limit(outPos + elementSz * len);
    818                 sout.put(sin);
    819                 break;
    820             case Int:
    821             case UnsignedInt:
    822                 IntBuffer iin = (IntBuffer) srcData;
    823                 IntBuffer iout = (IntBuffer) outVb.data;
    824                 iin.position(inPos).limit(inPos + elementSz * len);
    825                 iout.position(outPos).limit(outPos + elementSz * len);
    826                 iout.put(iin);
    827                 break;
    828             case Float:
    829                 FloatBuffer fin = (FloatBuffer) srcData;
    830                 FloatBuffer fout = (FloatBuffer) outVb.data;
    831                 fin.position(inPos).limit(inPos + elementSz * len);
    832                 fout.position(outPos).limit(outPos + elementSz * len);
    833                 fout.put(fin);
    834                 break;
    835             default:
    836                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
    837         }
    838 
    839         // Clear the output buffer to rewind it and reset its
    840         // limit from where we shortened it above.
    841         outVb.data.clear();
    842     }
    843 
    844     /**
    845      * Creates a {@link Buffer} that satisfies the given type and size requirements
    846      * of the parameters. The buffer will be of the type specified by
    847      * {@link Format format} and would be able to contain the given number
    848      * of elements with the given number of components in each element.
    849      */
    850     public static Buffer createBuffer(Format format, int components, int numElements){
    851         if (components < 1 || components > 4)
    852             throw new IllegalArgumentException("Num components must be between 1 and 4");
    853 
    854         int total = numElements * components;
    855 
    856         switch (format){
    857             case Byte:
    858             case UnsignedByte:
    859                 return BufferUtils.createByteBuffer(total);
    860             case Half:
    861                 return BufferUtils.createByteBuffer(total * 2);
    862             case Short:
    863             case UnsignedShort:
    864                 return BufferUtils.createShortBuffer(total);
    865             case Int:
    866             case UnsignedInt:
    867                 return BufferUtils.createIntBuffer(total);
    868             case Float:
    869                 return BufferUtils.createFloatBuffer(total);
    870             case Double:
    871                 return BufferUtils.createDoubleBuffer(total);
    872             default:
    873                 throw new UnsupportedOperationException("Unrecoginized buffer format: "+format);
    874         }
    875     }
    876 
    877     /**
    878      * Creates a deep clone of the {@link VertexBuffer}.
    879      *
    880      * @return Deep clone of this buffer
    881      */
    882     @Override
    883     public VertexBuffer clone(){
    884         // NOTE: Superclass GLObject automatically creates shallow clone
    885         // e.g re-use ID.
    886         VertexBuffer vb = (VertexBuffer) super.clone();
    887         vb.handleRef = new Object();
    888         vb.id = -1;
    889         if (data != null) {
    890             // Make sure to pass a read-only buffer to clone so that
    891             // the position information doesn't get clobbered by another
    892             // reading thread during cloning (and vice versa) since this is
    893             // a purely read-only operation.
    894             vb.updateData(BufferUtils.clone(getDataReadOnly()));
    895         }
    896 
    897         return vb;
    898     }
    899 
    900     /**
    901      * Creates a deep clone of this VertexBuffer but overrides the
    902      * {@link Type}.
    903      *
    904      * @param overrideType The type of the cloned VertexBuffer
    905      * @return A deep clone of the buffer
    906      */
    907     public VertexBuffer clone(Type overrideType){
    908         VertexBuffer vb = new VertexBuffer(overrideType);
    909         vb.components = components;
    910         vb.componentsLength = componentsLength;
    911 
    912         // Make sure to pass a read-only buffer to clone so that
    913         // the position information doesn't get clobbered by another
    914         // reading thread during cloning (and vice versa) since this is
    915         // a purely read-only operation.
    916         vb.data = BufferUtils.clone(getDataReadOnly());
    917         vb.format = format;
    918         vb.handleRef = new Object();
    919         vb.id = -1;
    920         vb.normalized = normalized;
    921         vb.offset = offset;
    922         vb.stride = stride;
    923         vb.updateNeeded = true;
    924         vb.usage = usage;
    925         return vb;
    926     }
    927 
    928     @Override
    929     public String toString(){
    930         String dataTxt = null;
    931         if (data != null){
    932             dataTxt = ", elements="+data.capacity();
    933         }
    934         return getClass().getSimpleName() + "[fmt="+format.name()
    935                                             +", type="+bufType.name()
    936                                             +", usage="+usage.name()
    937                                             +dataTxt+"]";
    938     }
    939 
    940     @Override
    941     public void resetObject() {
    942 //        assert this.id != -1;
    943         this.id = -1;
    944         setUpdateNeeded();
    945     }
    946 
    947     @Override
    948     public void deleteObject(Object rendererObject) {
    949         ((Renderer)rendererObject).deleteBuffer(this);
    950     }
    951 
    952     @Override
    953     public NativeObject createDestructableClone(){
    954         return new VertexBuffer(id);
    955     }
    956 
    957     public void write(JmeExporter ex) throws IOException {
    958         OutputCapsule oc = ex.getCapsule(this);
    959         oc.write(components, "components", 0);
    960         oc.write(usage, "usage", Usage.Dynamic);
    961         oc.write(bufType, "buffer_type", null);
    962         oc.write(format, "format", Format.Float);
    963         oc.write(normalized, "normalized", false);
    964         oc.write(offset, "offset", 0);
    965         oc.write(stride, "stride", 0);
    966 
    967         String dataName = "data" + format.name();
    968         Buffer roData = getDataReadOnly();
    969         switch (format){
    970             case Float:
    971                 oc.write((FloatBuffer) roData, dataName, null);
    972                 break;
    973             case Short:
    974             case UnsignedShort:
    975                 oc.write((ShortBuffer) roData, dataName, null);
    976                 break;
    977             case UnsignedByte:
    978             case Byte:
    979             case Half:
    980                 oc.write((ByteBuffer) roData, dataName, null);
    981                 break;
    982             case Int:
    983             case UnsignedInt:
    984                 oc.write((IntBuffer) roData, dataName, null);
    985                 break;
    986             default:
    987                 throw new IOException("Unsupported export buffer format: "+format);
    988         }
    989     }
    990 
    991     public void read(JmeImporter im) throws IOException {
    992         InputCapsule ic = im.getCapsule(this);
    993         components = ic.readInt("components", 0);
    994         usage = ic.readEnum("usage", Usage.class, Usage.Dynamic);
    995         bufType = ic.readEnum("buffer_type", Type.class, null);
    996         format = ic.readEnum("format", Format.class, Format.Float);
    997         normalized = ic.readBoolean("normalized", false);
    998         offset = ic.readInt("offset", 0);
    999         stride = ic.readInt("stride", 0);
   1000         componentsLength = components * format.getComponentSize();
   1001 
   1002         String dataName = "data" + format.name();
   1003         switch (format){
   1004             case Float:
   1005                 data = ic.readFloatBuffer(dataName, null);
   1006                 break;
   1007             case Short:
   1008             case UnsignedShort:
   1009                 data = ic.readShortBuffer(dataName, null);
   1010                 break;
   1011             case UnsignedByte:
   1012             case Byte:
   1013             case Half:
   1014                 data = ic.readByteBuffer(dataName, null);
   1015                 break;
   1016             case Int:
   1017             case UnsignedInt:
   1018                 data = ic.readIntBuffer(dataName, null);
   1019                 break;
   1020             default:
   1021                 throw new IOException("Unsupported import buffer format: "+format);
   1022         }
   1023     }
   1024 
   1025 }
   1026