OSDN Git Service

* Added null checks in setupData()
[mikumikustudio/MikuMikuStudio.git] / engine / src / core / com / jme3 / scene / VertexBuffer.java
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.JmeExporter;
36 import com.jme3.export.JmeImporter;
37 import com.jme3.export.InputCapsule;
38 import com.jme3.export.OutputCapsule;
39 import com.jme3.export.Savable;
40 import com.jme3.math.FastMath;
41 import com.jme3.renderer.GLObject;
42 import com.jme3.renderer.Renderer;
43 import com.jme3.util.BufferUtils;
44 import java.io.IOException;
45 import java.nio.Buffer;
46 import java.nio.ByteBuffer;
47 import java.nio.FloatBuffer;
48 import java.nio.IntBuffer;
49 import java.nio.ShortBuffer;
50
51 /**
52  * A <code>VertexBuffer</code> contains a particular type of geometry
53  * data used by {@link Mesh}es. Every VertexBuffer set on a <code>Mesh</code>
54  * is sent as an attribute to the vertex shader to be processed.
55  */
56 public class VertexBuffer extends GLObject implements Savable, Cloneable {
57
58     /**
59      * Type of buffer. Specifies the actual attribute it defines.
60      */
61     public static enum Type {
62         /**
63          * Position of the vertex (3 floats)
64          */
65         Position,
66
67         /**
68          * The size of the point when using point buffers.
69          */
70         Size,
71
72         /**
73          * Normal vector, normalized.
74          */
75         Normal,
76
77         /**
78          * Texture coordinate
79          */
80         TexCoord,
81
82         /**
83          * Color and Alpha (4 floats)
84          */
85         Color,
86
87         /**
88          * Tangent vector, normalized.
89          */
90         Tangent,
91
92         /**
93          * Binormal vector, normalized.
94          */
95         Binormal,
96
97         /**
98          * Specifies the source data for various vertex buffers
99          * when interleaving is used.
100          */
101         InterleavedData,
102
103         /**
104          * Do not use.
105          */
106         @Deprecated
107         MiscAttrib,
108
109         /**
110          * Specifies the index buffer, must contain integer data.
111          */
112         Index,
113
114         /** 
115          * Inital vertex position, used with animation 
116          */
117         BindPosePosition,
118
119         /** 
120          * Inital vertex normals, used with animation
121          */
122         BindPoseNormal,
123
124         /** 
125          * Bone weights, used with animation
126          */
127         BoneWeight,
128
129         /** 
130          * Bone indices, used with animation
131          */
132         BoneIndex,
133
134         /**
135          * Texture coordinate #2
136          */
137         TexCoord2,
138
139         /**
140          * Texture coordinate #3
141          */
142         TexCoord3,
143
144         /**
145          * Texture coordinate #4
146          */
147         TexCoord4,
148
149         /**
150          * Texture coordinate #5
151          */
152         TexCoord5,
153
154         /**
155          * Texture coordinate #6
156          */
157         TexCoord6,
158
159         /**
160          * Texture coordinate #7
161          */
162         TexCoord7,
163
164         /**
165          * Texture coordinate #8
166          */
167         TexCoord8,
168     }
169
170     /**
171      * The usage of the VertexBuffer, specifies how often the buffer
172      * is used. This can determine if a vertex buffer is placed in VRAM
173      * or held in video memory, but no guarantees are made- it's only a hint.
174      */
175     public static enum Usage {
176         
177         /**
178          * Mesh data is sent once and very rarely updated.
179          */
180         Static,
181
182         /**
183          * Mesh data is updated occasionally (once per frame or less).
184          */
185         Dynamic,
186
187         /**
188          * Mesh data is updated every frame.
189          */
190         Stream,
191
192         /**
193          * Mesh data is not sent to GPU at all. It is only
194          * used by the CPU.
195          */
196         CpuOnly;
197     }
198
199     public static enum Format {
200         // Floating point formats
201         Half(2),
202         Float(4),
203         Double(8),
204
205         // Integer formats
206         Byte(1),
207         UnsignedByte(1),
208         Short(2),
209         UnsignedShort(2),
210         Int(4),
211         UnsignedInt(4);
212
213         private int componentSize = 0;
214
215         Format(int componentSize){
216             this.componentSize = componentSize;
217         }
218
219         /**
220          * @return Size in bytes of this data type.
221          */
222         public int getComponentSize(){
223             return componentSize;
224         }
225     }
226
227     protected int offset = 0;
228     protected int lastLimit = 0;
229     protected int stride = 0;
230     protected int components = 0;
231
232     /**
233      * derived from components * format.getComponentSize()
234      */
235     protected transient int componentsLength = 0;
236     protected Buffer data = null;
237     protected Usage usage;
238     protected Type bufType;
239     protected Format format;
240     protected boolean normalized = false;
241     protected transient boolean dataSizeChanged = false;
242
243     /**
244      * Creates an empty, uninitialized buffer.
245      * Must call setupData() to initialize.
246      */
247     public VertexBuffer(Type type){
248         super(GLObject.Type.VertexBuffer);
249         this.bufType = type;
250     }
251
252     /**
253      * Do not use this constructor. Serialization purposes only.
254      */
255     public VertexBuffer(){
256         super(GLObject.Type.VertexBuffer);
257     }
258
259     protected VertexBuffer(int id){
260         super(GLObject.Type.VertexBuffer, id);
261     }
262
263     /**
264      * @return The offset (in bytes) from the start of the buffer
265      * after which the data is sent to the GPU.
266      */
267     public int getOffset() {
268         return offset;
269     }
270
271     /**
272      * @param offset Specify the offset (in bytes) from the start of the buffer
273      * after which the data is sent to the GPU.
274      */
275     public void setOffset(int offset) {
276         this.offset = offset;
277     }
278
279     /**
280      * @return The stride (in bytes) for the data. If the data is packed
281      * in the buffer, then stride is 0, if there's other data that is between
282      * the current component and the next component in the buffer, then this
283      * specifies the size in bytes of that additional data.
284      */
285     public int getStride() {
286         return stride;
287     }
288
289     /**
290      * @param stride The stride (in bytes) for the data. If the data is packed
291      * in the buffer, then stride is 0, if there's other data that is between
292      * the current component and the next component in the buffer, then this
293      * specifies the size in bytes of that additional data.
294      */
295     public void setStride(int stride) {
296         this.stride = stride;
297     }
298
299     /**
300      * @return A native buffer, in the specified {@link Format format}.
301      */
302     public Buffer getData(){
303         return data;
304     }
305
306     /**
307      * @return The usage of this buffer. See {@link Usage} for more
308      * information.
309      */
310     public Usage getUsage(){
311         return usage;
312     }
313
314     /**
315      * @param usage The usage of this buffer. See {@link Usage} for more
316      * information.
317      */
318     public void setUsage(Usage usage){
319 //        if (id != -1)
320 //            throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
321
322         this.usage = usage;
323     }
324
325     /**
326      * @param normalized Set to true if integer components should be converted
327      * from their maximal range into the range 0.0 - 1.0 when converted to
328      * a floating-point value for the shader.
329      * E.g. if the {@link Format} is {@link Format#UnsignedInt}, then
330      * the components will be converted to the range 0.0 - 1.0 by dividing
331      * every integer by 2^32.
332      */
333     public void setNormalized(boolean normalized){
334         this.normalized = normalized;
335     }
336
337     /**
338      * @return True if integer components should be converted to the range 0-1.
339      * @see VertexBuffer#setNormalized(boolean) 
340      */
341     public boolean isNormalized(){
342         return normalized;
343     }
344
345     /**
346      * @return The type of information that this buffer has.
347      */
348     public Type getBufferType(){
349         return bufType;
350     }
351
352     /**
353      * @return The {@link Format format}, or data type of the data.
354      */
355     public Format getFormat(){
356         return format;
357     }
358
359     /**
360      * @return The number of components of the given {@link Format format} per
361      * element.
362      */
363     public int getNumComponents(){
364         return components;
365     }
366
367     /**
368      * @return The total number of data elements in the data buffer.
369      */
370     public int getNumElements(){
371         int elements = data.capacity() / components;
372         if (format == Format.Half)
373             elements /= 2;
374         return elements;
375     }
376
377     /**
378      * Called to initialize the data in the <code>VertexBuffer</code>. Must only
379      * be called once.
380      * 
381      * @param usage The usage for the data, or how often will the data
382      * be updated per frame. See the {@link Usage} enum.
383      * @param components The number of components per element.
384      * @param format The {@link Format format}, or data-type of a single
385      * component.
386      * @param data A native buffer, the format of which matches the {@link Format}
387      * argument.
388      */
389     public void setupData(Usage usage, int components, Format format, Buffer data){
390         if (id != -1)
391             throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
392
393         if (usage == null || format == null || data == null)
394             throw new IllegalArgumentException("None of the arguments can be null");
395             
396         if (components < 1 || components > 4)
397             throw new IllegalArgumentException("components must be between 1 and 4");
398
399         this.data = data;
400         this.components = components;
401         this.usage = usage;
402         this.format = format;
403         this.componentsLength = components * format.getComponentSize();
404         this.lastLimit = data.limit();
405         setUpdateNeeded();
406     }
407
408     /**
409      * Called to update the data in the buffer with new data. Can only
410      * be called after {@link VertexBuffer#setupData(com.jme3.scene.VertexBuffer.Usage, int, com.jme3.scene.VertexBuffer.Format, java.nio.Buffer) }
411      * has been called. Note that it is fine to call this method on the
412      * data already set, e.g. vb.updateData(vb.getData()), this will just
413      * set the proper update flag indicating the data should be sent to the GPU
414      * again.
415      * It is allowed to specify a buffer with different capacity than the
416      * originally set buffer.
417      *
418      * @param data The data buffer to set
419      */
420     public void updateData(Buffer data){
421         if (id != -1){
422             // request to update data is okay
423         }
424
425         // will force renderer to call glBufferData again
426         if (this.data.getClass() != data.getClass() || data.limit() != lastLimit){
427             dataSizeChanged = true;
428             lastLimit = data.limit();
429         }
430         this.data = data;
431         setUpdateNeeded();
432     }
433
434     public boolean hasDataSizeChanged() {
435         return dataSizeChanged;
436     }
437
438     @Override
439     public void clearUpdateNeeded(){
440         super.clearUpdateNeeded();
441         dataSizeChanged = false;
442     }
443
444     /**
445      * Converts single floating-point data to {@link Format#Half half} floating-point data.
446      */
447     public void convertToHalf(){
448         if (id != -1)
449             throw new UnsupportedOperationException("Data has already been sent.");
450
451         if (format != Format.Float)
452             throw new IllegalStateException("Format must be float!");
453
454         int numElements = data.capacity() / components;
455         format = Format.Half;
456         this.componentsLength = components * format.getComponentSize();
457         
458         ByteBuffer halfData = BufferUtils.createByteBuffer(componentsLength * numElements);
459         halfData.rewind();
460
461         FloatBuffer floatData = (FloatBuffer) data;
462         floatData.rewind();
463
464         for (int i = 0; i < floatData.capacity(); i++){
465             float f = floatData.get(i);
466             short half = FastMath.convertFloatToHalf(f);
467             halfData.putShort(half);
468         }
469         this.data = halfData;
470         setUpdateNeeded();
471         dataSizeChanged = true;
472     }
473
474     /**
475      * Reduces the capacity of the buffer to the given amount
476      * of elements, any elements at the end of the buffer are truncated
477      * as necessary.
478      *
479      * @param numElements
480      */
481     public void compact(int numElements){
482         int total = components * numElements;
483         data.clear();
484         switch (format){
485             case Byte:
486             case UnsignedByte:
487             case Half:
488                 ByteBuffer bbuf = (ByteBuffer) data;
489                 bbuf.limit(total);
490                 ByteBuffer bnewBuf = BufferUtils.createByteBuffer(total);
491                 bnewBuf.put(bbuf);
492                 data = bnewBuf;
493                 break;
494             case Short:
495             case UnsignedShort:
496                 ShortBuffer sbuf = (ShortBuffer) data;
497                 sbuf.limit(total);
498                 ShortBuffer snewBuf = BufferUtils.createShortBuffer(total);
499                 snewBuf.put(sbuf);
500                 data = snewBuf;
501                 break;
502             case Int:
503             case UnsignedInt:
504                 IntBuffer ibuf = (IntBuffer) data;
505                 ibuf.limit(total);
506                 IntBuffer inewBuf = BufferUtils.createIntBuffer(total);
507                 inewBuf.put(ibuf);
508                 data = inewBuf;
509                 break;
510             case Float:
511                 FloatBuffer fbuf = (FloatBuffer) data;
512                 fbuf.limit(total);
513                 FloatBuffer fnewBuf = BufferUtils.createFloatBuffer(total);
514                 fnewBuf.put(fbuf);
515                 data = fnewBuf;
516                 break;
517             default:
518                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
519         }
520         data.clear();
521         setUpdateNeeded();
522         dataSizeChanged = true;
523     }
524
525     public void setElementComponent(int elementIndex, int componentIndex, Object val){
526         int inPos = elementIndex * components;
527         int elementPos = componentIndex;
528
529         if (format == Format.Half){
530             inPos *= 2;
531             elementPos *= 2;
532         }
533
534         data.clear();
535
536         switch (format){
537             case Byte:
538             case UnsignedByte:
539             case Half:
540                 ByteBuffer bin = (ByteBuffer) data;
541                 bin.put(inPos + elementPos, (Byte)val);
542                 break;
543             case Short:
544             case UnsignedShort:
545                 ShortBuffer sin = (ShortBuffer) data;
546                 sin.put(inPos + elementPos, (Short)val);
547                 break;
548             case Int:
549             case UnsignedInt:
550                 IntBuffer iin = (IntBuffer) data;
551                 iin.put(inPos + elementPos, (Integer)val);
552                 break;
553             case Float:
554                 FloatBuffer fin = (FloatBuffer) data;
555                 fin.put(inPos + elementPos, (Float)val);
556                 break;
557             default:
558                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
559         }
560     }
561
562     public Object getElementComponent(int elementIndex, int componentIndex){
563         int inPos = elementIndex * components;
564         int elementPos = componentIndex;
565
566         if (format == Format.Half){
567             inPos *= 2;
568             elementPos *= 2;
569         }
570
571         data.clear();
572
573         switch (format){
574             case Byte:
575             case UnsignedByte:
576             case Half:
577                 ByteBuffer bin = (ByteBuffer) data;
578                 return bin.get(inPos + elementPos);
579             case Short:
580             case UnsignedShort:
581                 ShortBuffer sin = (ShortBuffer) data;
582                 return sin.get(inPos + elementPos);
583             case Int:
584             case UnsignedInt:
585                 IntBuffer iin = (IntBuffer) data;
586                 return iin.get(inPos + elementPos);
587             case Float:
588                 FloatBuffer fin = (FloatBuffer) data;
589                 return fin.get(inPos + elementPos);
590             default:
591                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
592         }
593     }
594
595     /**
596      * Copies a single element of data from this <code>VertexBuffer</code>
597      * to the given output VertexBuffer.
598      * 
599      * @param inIndex
600      * @param outVb
601      * @param outIndex
602      */
603     public void copyElement(int inIndex, VertexBuffer outVb, int outIndex){
604         if (outVb.format != format || outVb.components != components)
605             throw new IllegalArgumentException("Buffer format mismatch. Cannot copy");
606
607         int inPos  = inIndex  * components;
608         int outPos = outIndex * components;
609         int elementSz = components;
610         if (format == Format.Half){
611             // because half is stored as bytebuf but its 2 bytes long
612             inPos *= 2;
613             outPos *= 2;
614             elementSz *= 2;
615         }
616
617         data.clear();
618         outVb.data.clear();
619
620         switch (format){
621             case Byte:
622             case UnsignedByte:
623             case Half:
624                 ByteBuffer bin = (ByteBuffer) data;
625                 ByteBuffer bout = (ByteBuffer) outVb.data;
626                 bin.position(inPos).limit(inPos + elementSz);
627                 bout.position(outPos).limit(outPos + elementSz);
628                 bout.put(bin);
629                 break;
630             case Short:
631             case UnsignedShort:
632                 ShortBuffer sin = (ShortBuffer) data;
633                 ShortBuffer sout = (ShortBuffer) outVb.data;
634                 sin.position(inPos).limit(inPos + elementSz);
635                 sout.position(outPos).limit(outPos + elementSz);
636                 sout.put(sin);
637                 break;
638             case Int:
639             case UnsignedInt:
640                 IntBuffer iin = (IntBuffer) data;
641                 IntBuffer iout = (IntBuffer) outVb.data;
642                 iin.position(inPos).limit(inPos + elementSz);
643                 iout.position(outPos).limit(outPos + elementSz);
644                 iout.put(iin);
645                 break;
646             case Float:
647                 FloatBuffer fin = (FloatBuffer) data;
648                 FloatBuffer fout = (FloatBuffer) outVb.data;
649                 fin.position(inPos).limit(inPos + elementSz);
650                 fout.position(outPos).limit(outPos + elementSz);
651                 fout.put(fin);
652                 break;
653             default:
654                 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
655         }
656
657         data.clear();
658         outVb.data.clear();
659     }
660
661     /**
662      * Creates a {@link Buffer} that satisfies the given type and size requirements
663      * of the parameters. The buffer will be of the type specified by
664      * {@link Format format} and would be able to contain the given number
665      * of elements with the given number of components in each element.
666      *
667      * @param format
668      * @param components
669      * @param numElements
670      * @return
671      */
672     public static Buffer createBuffer(Format format, int components, int numElements){
673         if (components < 1 || components > 4)
674             throw new IllegalArgumentException("Num components must be between 1 and 4");
675
676         int total = numElements * components;
677
678         switch (format){
679             case Byte:
680             case UnsignedByte:
681                 return BufferUtils.createByteBuffer(total);
682             case Half:
683                 return BufferUtils.createByteBuffer(total * 2);
684             case Short:
685             case UnsignedShort:
686                 return BufferUtils.createShortBuffer(total);
687             case Int:
688             case UnsignedInt:
689                 return BufferUtils.createIntBuffer(total);
690             case Float:
691                 return BufferUtils.createFloatBuffer(total);
692             case Double:
693                 return BufferUtils.createDoubleBuffer(total);
694             default:
695                 throw new UnsupportedOperationException("Unrecoginized buffer format: "+format);
696         }
697     }
698
699     @Override
700     public VertexBuffer clone(){
701         // NOTE: Superclass GLObject automatically creates shallow clone
702         // e.g re-use ID.
703         VertexBuffer vb = (VertexBuffer) super.clone();
704         vb.handleRef = new Object();
705         vb.id = -1;
706         if (data != null)
707             vb.updateData(BufferUtils.clone(data));
708         
709         return vb;
710     }
711
712     public VertexBuffer clone(Type overrideType){
713         VertexBuffer vb = new VertexBuffer(overrideType);
714         vb.components = components;
715         vb.componentsLength = componentsLength;
716         vb.data = BufferUtils.clone(data);
717         vb.format = format;
718         vb.handleRef = new Object();
719         vb.id = -1;
720         vb.normalized = normalized;
721         vb.offset = offset;
722         vb.stride = stride;
723         vb.updateNeeded = true;
724         vb.usage = usage;
725         return vb;
726     }
727
728     @Override
729     public String toString(){
730         String dataTxt = null;
731         if (data != null){
732             dataTxt = ", elements="+data.capacity();
733         }
734         return getClass().getSimpleName() + "[fmt="+format.name()
735                                             +", type="+bufType.name()
736                                             +", usage="+usage.name()
737                                             +dataTxt+"]";
738     }
739
740     @Override
741     public void resetObject() {
742 //        assert this.id != -1;
743         this.id = -1;
744         setUpdateNeeded();
745     }
746
747     @Override
748     public void deleteObject(Renderer r) {
749         r.deleteBuffer(this);
750     }
751
752     @Override
753     public GLObject createDestructableClone(){
754         return new VertexBuffer(id);
755     }
756
757     public void write(JmeExporter ex) throws IOException {
758         OutputCapsule oc = ex.getCapsule(this);
759         oc.write(components, "components", 0);
760         oc.write(usage, "usage", Usage.Dynamic);
761         oc.write(bufType, "buffer_type", null);
762         oc.write(format, "format", Format.Float);
763         oc.write(normalized, "normalized", false);
764         oc.write(offset, "offset", 0);
765         oc.write(stride, "stride", 0);
766
767         String dataName = "data" + format.name();
768         switch (format){
769             case Float:
770                 oc.write((FloatBuffer) data, dataName, null);
771                 break;
772             case Short:
773             case UnsignedShort:
774                 oc.write((ShortBuffer) data, dataName, null);
775                 break;
776             case UnsignedByte:
777             case Byte:
778             case Half:
779                 oc.write((ByteBuffer) data, dataName, null);
780                 break;
781             case Int:
782             case UnsignedInt:
783                 oc.write((IntBuffer) data, dataName, null);
784                 break;
785             default:
786                 throw new IOException("Unsupported export buffer format: "+format);
787         }
788     }
789
790     public void read(JmeImporter im) throws IOException {
791         InputCapsule ic = im.getCapsule(this);
792         components = ic.readInt("components", 0);
793         usage = ic.readEnum("usage", Usage.class, Usage.Dynamic);
794         bufType = ic.readEnum("buffer_type", Type.class, null);
795         format = ic.readEnum("format", Format.class, Format.Float);
796         normalized = ic.readBoolean("normalized", false);
797         offset = ic.readInt("offset", 0);
798         stride = ic.readInt("stride", 0);
799         componentsLength = components * format.getComponentSize();
800
801         String dataName = "data" + format.name();
802         switch (format){
803             case Float:
804                 data = ic.readFloatBuffer(dataName, null);
805                 break;
806             case Short:
807             case UnsignedShort:
808                 data = ic.readShortBuffer(dataName, null);
809                 break;
810             case UnsignedByte:
811             case Byte:
812             case Half:
813                 data = ic.readByteBuffer(dataName, null);
814                 break;
815             case Int:
816             case UnsignedInt:
817                 data = ic.readIntBuffer(dataName, null);
818                 break;
819             default:
820                 throw new IOException("Unsupported import buffer format: "+format);
821         }
822     }
823
824 }