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 
Format(int componentSize)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          */
getComponentSize()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      */
VertexBuffer(Type type)321     public VertexBuffer(Type type){
322         super(VertexBuffer.class);
323         this.bufType = type;
324     }
325 
326     /**
327      * Serialization only. Do not use.
328      */
VertexBuffer()329     public VertexBuffer(){
330         super(VertexBuffer.class);
331     }
332 
VertexBuffer(int id)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      */
getOffset()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      */
setOffset(int offset)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      */
getStride()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      */
setStride(int stride)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      */
getData()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      */
getDataReadOnly()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      */
getUsage()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      */
setUsage(Usage usage)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      */
setNormalized(boolean normalized)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      */
isNormalized()468     public boolean isNormalized(){
469         return normalized;
470     }
471 
472     /**
473      * @return The type of information that this buffer has.
474      */
getBufferType()475     public Type getBufferType(){
476         return bufType;
477     }
478 
479     /**
480      * @return The {@link Format format}, or data type of the data.
481      */
getFormat()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      */
getNumComponents()490     public int getNumComponents(){
491         return components;
492     }
493 
494     /**
495      * @return The total number of data elements in the data buffer.
496      */
getNumElements()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      */
setupData(Usage usage, int components, Format format, Buffer data)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      */
updateData(Buffer data)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      */
hasDataSizeChanged()576     public boolean hasDataSizeChanged() {
577         return dataSizeChanged;
578     }
579 
580     @Override
clearUpdateNeeded()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      */
convertToHalf()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      */
compact(int numElements)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      */
setElementComponent(int elementIndex, int componentIndex, Object val)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      */
getElementComponent(int elementIndex, int componentIndex)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      */
copyElement(int inIndex, VertexBuffer outVb, int outIndex)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      */
copyElements(int inIndex, VertexBuffer outVb, int outIndex, int len)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      */
createBuffer(Format format, int components, int numElements)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
clone()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      */
clone(Type overrideType)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
toString()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
resetObject()941     public void resetObject() {
942 //        assert this.id != -1;
943         this.id = -1;
944         setUpdateNeeded();
945     }
946 
947     @Override
deleteObject(Object rendererObject)948     public void deleteObject(Object rendererObject) {
949         ((Renderer)rendererObject).deleteBuffer(this);
950     }
951 
952     @Override
createDestructableClone()953     public NativeObject createDestructableClone(){
954         return new VertexBuffer(id);
955     }
956 
write(JmeExporter ex)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 
read(JmeImporter im)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