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