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
33package com.jme3.scene;
34
35import com.jme3.export.*;
36import com.jme3.math.FastMath;
37import com.jme3.renderer.Renderer;
38import com.jme3.util.BufferUtils;
39import com.jme3.util.NativeObject;
40import java.io.IOException;
41import 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 */
57public 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