2 * Copyright (c) 2009-2010 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
33 package com.jme3.scene;
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;
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.
56 public class VertexBuffer extends GLObject implements Savable, Cloneable {
59 * Type of buffer. Specifies the actual attribute it defines.
61 public static enum Type {
63 * Position of the vertex (3 floats)
68 * The size of the point when using point buffers.
73 * Normal vector, normalized.
83 * Color and Alpha (4 floats)
88 * Tangent vector, normalized.
93 * Binormal vector, normalized.
98 * Specifies the source data for various vertex buffers
99 * when interleaving is used.
110 * Specifies the index buffer, must contain integer data.
115 * Inital vertex position, used with animation
120 * Inital vertex normals, used with animation
125 * Bone weights, used with animation
130 * Bone indices, used with animation
135 * Texture coordinate #2
140 * Texture coordinate #3
145 * Texture coordinate #4
150 * Texture coordinate #5
155 * Texture coordinate #6
160 * Texture coordinate #7
165 * Texture coordinate #8
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.
175 public static enum Usage {
178 * Mesh data is sent once and very rarely updated.
183 * Mesh data is updated occasionally (once per frame or less).
188 * Mesh data is updated every frame.
193 * Mesh data is not sent to GPU at all. It is only
199 public static enum Format {
200 // Floating point formats
213 private int componentSize = 0;
215 Format(int componentSize){
216 this.componentSize = componentSize;
220 * @return Size in bytes of this data type.
222 public int getComponentSize(){
223 return componentSize;
227 protected int offset = 0;
228 protected int lastLimit = 0;
229 protected int stride = 0;
230 protected int components = 0;
233 * derived from components * format.getComponentSize()
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;
244 * Creates an empty, uninitialized buffer.
245 * Must call setupData() to initialize.
247 public VertexBuffer(Type type){
248 super(GLObject.Type.VertexBuffer);
253 * Do not use this constructor. Serialization purposes only.
255 public VertexBuffer(){
256 super(GLObject.Type.VertexBuffer);
259 protected VertexBuffer(int id){
260 super(GLObject.Type.VertexBuffer, id);
264 * @return The offset (in bytes) from the start of the buffer
265 * after which the data is sent to the GPU.
267 public int getOffset() {
272 * @param offset Specify the offset (in bytes) from the start of the buffer
273 * after which the data is sent to the GPU.
275 public void setOffset(int offset) {
276 this.offset = offset;
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.
285 public int getStride() {
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.
295 public void setStride(int stride) {
296 this.stride = stride;
300 * @return A native buffer, in the specified {@link Format format}.
302 public Buffer getData(){
307 * @return The usage of this buffer. See {@link Usage} for more
310 public Usage getUsage(){
315 * @param usage The usage of this buffer. See {@link Usage} for more
318 public void setUsage(Usage usage){
320 // throw new UnsupportedOperationException("Data has already been sent. Cannot set usage.");
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.
333 public void setNormalized(boolean normalized){
334 this.normalized = normalized;
338 * @return True if integer components should be converted to the range 0-1.
339 * @see VertexBuffer#setNormalized(boolean)
341 public boolean isNormalized(){
346 * @return The type of information that this buffer has.
348 public Type getBufferType(){
353 * @return The {@link Format format}, or data type of the data.
355 public Format getFormat(){
360 * @return The number of components of the given {@link Format format} per
363 public int getNumComponents(){
368 * @return The total number of data elements in the data buffer.
370 public int getNumElements(){
371 int elements = data.capacity() / components;
372 if (format == Format.Half)
378 * Called to initialize the data in the <code>VertexBuffer</code>. Must only
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
386 * @param data A native buffer, the format of which matches the {@link Format}
389 public void setupData(Usage usage, int components, Format format, Buffer data){
391 throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
393 if (usage == null || format == null || data == null)
394 throw new IllegalArgumentException("None of the arguments can be null");
396 if (components < 1 || components > 4)
397 throw new IllegalArgumentException("components must be between 1 and 4");
400 this.components = components;
402 this.format = format;
403 this.componentsLength = components * format.getComponentSize();
404 this.lastLimit = data.limit();
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
415 * It is allowed to specify a buffer with different capacity than the
416 * originally set buffer.
418 * @param data The data buffer to set
420 public void updateData(Buffer data){
422 // request to update data is okay
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();
434 public boolean hasDataSizeChanged() {
435 return dataSizeChanged;
439 public void clearUpdateNeeded(){
440 super.clearUpdateNeeded();
441 dataSizeChanged = false;
445 * Converts single floating-point data to {@link Format#Half half} floating-point data.
447 public void convertToHalf(){
449 throw new UnsupportedOperationException("Data has already been sent.");
451 if (format != Format.Float)
452 throw new IllegalStateException("Format must be float!");
454 int numElements = data.capacity() / components;
455 format = Format.Half;
456 this.componentsLength = components * format.getComponentSize();
458 ByteBuffer halfData = BufferUtils.createByteBuffer(componentsLength * numElements);
461 FloatBuffer floatData = (FloatBuffer) data;
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);
469 this.data = halfData;
471 dataSizeChanged = true;
475 * Reduces the capacity of the buffer to the given amount
476 * of elements, any elements at the end of the buffer are truncated
481 public void compact(int numElements){
482 int total = components * numElements;
488 ByteBuffer bbuf = (ByteBuffer) data;
490 ByteBuffer bnewBuf = BufferUtils.createByteBuffer(total);
496 ShortBuffer sbuf = (ShortBuffer) data;
498 ShortBuffer snewBuf = BufferUtils.createShortBuffer(total);
504 IntBuffer ibuf = (IntBuffer) data;
506 IntBuffer inewBuf = BufferUtils.createIntBuffer(total);
511 FloatBuffer fbuf = (FloatBuffer) data;
513 FloatBuffer fnewBuf = BufferUtils.createFloatBuffer(total);
518 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
522 dataSizeChanged = true;
525 public void setElementComponent(int elementIndex, int componentIndex, Object val){
526 int inPos = elementIndex * components;
527 int elementPos = componentIndex;
529 if (format == Format.Half){
540 ByteBuffer bin = (ByteBuffer) data;
541 bin.put(inPos + elementPos, (Byte)val);
545 ShortBuffer sin = (ShortBuffer) data;
546 sin.put(inPos + elementPos, (Short)val);
550 IntBuffer iin = (IntBuffer) data;
551 iin.put(inPos + elementPos, (Integer)val);
554 FloatBuffer fin = (FloatBuffer) data;
555 fin.put(inPos + elementPos, (Float)val);
558 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
562 public Object getElementComponent(int elementIndex, int componentIndex){
563 int inPos = elementIndex * components;
564 int elementPos = componentIndex;
566 if (format == Format.Half){
577 ByteBuffer bin = (ByteBuffer) data;
578 return bin.get(inPos + elementPos);
581 ShortBuffer sin = (ShortBuffer) data;
582 return sin.get(inPos + elementPos);
585 IntBuffer iin = (IntBuffer) data;
586 return iin.get(inPos + elementPos);
588 FloatBuffer fin = (FloatBuffer) data;
589 return fin.get(inPos + elementPos);
591 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
596 * Copies a single element of data from this <code>VertexBuffer</code>
597 * to the given output VertexBuffer.
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");
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
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);
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);
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);
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);
654 throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
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.
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");
676 int total = numElements * components;
681 return BufferUtils.createByteBuffer(total);
683 return BufferUtils.createByteBuffer(total * 2);
686 return BufferUtils.createShortBuffer(total);
689 return BufferUtils.createIntBuffer(total);
691 return BufferUtils.createFloatBuffer(total);
693 return BufferUtils.createDoubleBuffer(total);
695 throw new UnsupportedOperationException("Unrecoginized buffer format: "+format);
700 public VertexBuffer clone(){
701 // NOTE: Superclass GLObject automatically creates shallow clone
703 VertexBuffer vb = (VertexBuffer) super.clone();
704 vb.handleRef = new Object();
707 vb.updateData(BufferUtils.clone(data));
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);
718 vb.handleRef = new Object();
720 vb.normalized = normalized;
723 vb.updateNeeded = true;
729 public String toString(){
730 String dataTxt = null;
732 dataTxt = ", elements="+data.capacity();
734 return getClass().getSimpleName() + "[fmt="+format.name()
735 +", type="+bufType.name()
736 +", usage="+usage.name()
741 public void resetObject() {
742 // assert this.id != -1;
748 public void deleteObject(Renderer r) {
749 r.deleteBuffer(this);
753 public GLObject createDestructableClone(){
754 return new VertexBuffer(id);
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);
767 String dataName = "data" + format.name();
770 oc.write((FloatBuffer) data, dataName, null);
774 oc.write((ShortBuffer) data, dataName, null);
779 oc.write((ByteBuffer) data, dataName, null);
783 oc.write((IntBuffer) data, dataName, null);
786 throw new IOException("Unsupported export buffer format: "+format);
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();
801 String dataName = "data" + format.name();
804 data = ic.readFloatBuffer(dataName, null);
808 data = ic.readShortBuffer(dataName, null);
813 data = ic.readByteBuffer(dataName, null);
817 data = ic.readIntBuffer(dataName, null);
820 throw new IOException("Unsupported import buffer format: "+format);