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 package com.jme3.util; 33 34 import com.jme3.math.ColorRGBA; 35 import com.jme3.math.Quaternion; 36 import com.jme3.math.Vector2f; 37 import com.jme3.math.Vector3f; 38 import java.lang.reflect.InvocationTargetException; 39 import java.lang.reflect.Method; 40 import java.nio.*; 41 import java.util.ArrayList; 42 import java.util.Collections; 43 import java.util.Map; 44 import java.util.WeakHashMap; 45 import java.util.concurrent.atomic.AtomicBoolean; 46 import java.util.logging.Level; 47 import java.util.logging.Logger; 48 49 /** 50 * <code>BufferUtils</code> is a helper class for generating nio buffers from 51 * jME data classes such as Vectors and ColorRGBA. 52 * 53 * @author Joshua Slack 54 * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $ 55 */ 56 public final class BufferUtils { 57 58 private static final Map<Buffer, Object> trackingHash = Collections.synchronizedMap(new WeakHashMap<Buffer, Object>()); 59 private static final Object ref = new Object(); 60 61 // Note: a WeakHashMap is really bad here since the hashCode() and 62 // equals() behavior of buffers will vary based on their contents. 63 // As it stands, put()'ing an empty buffer will wipe out the last 64 // empty buffer with the same size. So any tracked memory calculations 65 // could be lying. 66 // Besides, the hashmap behavior isn't even being used here and 67 // yet the expense is still incurred. For example, a newly allocated 68 // 10,000 byte buffer will iterate through the whole buffer of 0's 69 // to calculate the hashCode and then potentially do it again to 70 // calculate the equals()... which by the way is guaranteed for 71 // every empty buffer of an existing size since they will always produce 72 // the same hashCode(). 73 // It would be better to just keep a straight list of weak references 74 // and clean out the dead every time a new buffer is allocated. 75 // WeakHashMap is doing that anyway... so there is no extra expense 76 // incurred. 77 // Recommend a ConcurrentLinkedQueue of WeakReferences since it 78 // supports the threading semantics required with little extra overhead. 79 private static final boolean trackDirectMemory = false; 80 81 /** 82 * Creates a clone of the given buffer. The clone's capacity is 83 * equal to the given buffer's limit. 84 * 85 * @param buf The buffer to clone 86 * @return The cloned buffer 87 */ clone(Buffer buf)88 public static Buffer clone(Buffer buf) { 89 if (buf instanceof FloatBuffer) { 90 return clone((FloatBuffer) buf); 91 } else if (buf instanceof ShortBuffer) { 92 return clone((ShortBuffer) buf); 93 } else if (buf instanceof ByteBuffer) { 94 return clone((ByteBuffer) buf); 95 } else if (buf instanceof IntBuffer) { 96 return clone((IntBuffer) buf); 97 } else if (buf instanceof DoubleBuffer) { 98 return clone((DoubleBuffer) buf); 99 } else { 100 throw new UnsupportedOperationException(); 101 } 102 } 103 onBufferAllocated(Buffer buffer)104 private static void onBufferAllocated(Buffer buffer){ 105 /* 106 StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 107 int initialIndex = 0; 108 109 for (int i = 0; i < stackTrace.length; i++){ 110 if (!stackTrace[i].getClassName().equals(BufferUtils.class.getName())){ 111 initialIndex = i; 112 break; 113 } 114 } 115 116 int allocated = buffer.capacity(); 117 int size = 0; 118 119 if (buffer instanceof FloatBuffer){ 120 size = 4; 121 }else if (buffer instanceof ShortBuffer){ 122 size = 2; 123 }else if (buffer instanceof ByteBuffer){ 124 size = 1; 125 }else if (buffer instanceof IntBuffer){ 126 size = 4; 127 }else if (buffer instanceof DoubleBuffer){ 128 size = 8; 129 } 130 131 allocated *= size; 132 133 for (int i = initialIndex; i < stackTrace.length; i++){ 134 StackTraceElement element = stackTrace[i]; 135 if (element.getClassName().startsWith("java")){ 136 break; 137 } 138 139 try { 140 Class clazz = Class.forName(element.getClassName()); 141 if (i == initialIndex){ 142 System.out.println(clazz.getSimpleName()+"."+element.getMethodName()+"():" + element.getLineNumber() + " allocated " + allocated); 143 }else{ 144 System.out.println(" at " + clazz.getSimpleName()+"."+element.getMethodName()+"()"); 145 } 146 } catch (ClassNotFoundException ex) { 147 } 148 }*/ 149 150 if (trackDirectMemory){ 151 trackingHash.put(buffer, ref); 152 } 153 } 154 155 /** 156 * Generate a new FloatBuffer using the given array of Vector3f objects. 157 * The FloatBuffer will be 3 * data.length long and contain the vector data 158 * as data[0].x, data[0].y, data[0].z, data[1].x... etc. 159 * 160 * @param data array of Vector3f objects to place into a new FloatBuffer 161 */ createFloatBuffer(Vector3f... data)162 public static FloatBuffer createFloatBuffer(Vector3f... data) { 163 if (data == null) { 164 return null; 165 } 166 FloatBuffer buff = createFloatBuffer(3 * data.length); 167 for (int x = 0; x < data.length; x++) { 168 if (data[x] != null) { 169 buff.put(data[x].x).put(data[x].y).put(data[x].z); 170 } else { 171 buff.put(0).put(0).put(0); 172 } 173 } 174 buff.flip(); 175 return buff; 176 } 177 178 /** 179 * Generate a new FloatBuffer using the given array of Quaternion objects. 180 * The FloatBuffer will be 4 * data.length long and contain the vector data. 181 * 182 * @param data array of Quaternion objects to place into a new FloatBuffer 183 */ createFloatBuffer(Quaternion... data)184 public static FloatBuffer createFloatBuffer(Quaternion... data) { 185 if (data == null) { 186 return null; 187 } 188 FloatBuffer buff = createFloatBuffer(4 * data.length); 189 for (int x = 0; x < data.length; x++) { 190 if (data[x] != null) { 191 buff.put(data[x].getX()).put(data[x].getY()).put(data[x].getZ()).put(data[x].getW()); 192 } else { 193 buff.put(0).put(0).put(0); 194 } 195 } 196 buff.flip(); 197 return buff; 198 } 199 200 /** 201 * Generate a new FloatBuffer using the given array of float primitives. 202 * @param data array of float primitives to place into a new FloatBuffer 203 */ createFloatBuffer(float... data)204 public static FloatBuffer createFloatBuffer(float... data) { 205 if (data == null) { 206 return null; 207 } 208 FloatBuffer buff = createFloatBuffer(data.length); 209 buff.clear(); 210 buff.put(data); 211 buff.flip(); 212 return buff; 213 } 214 215 /** 216 * Create a new FloatBuffer of an appropriate size to hold the specified 217 * number of Vector3f object data. 218 * 219 * @param vertices 220 * number of vertices that need to be held by the newly created 221 * buffer 222 * @return the requested new FloatBuffer 223 */ createVector3Buffer(int vertices)224 public static FloatBuffer createVector3Buffer(int vertices) { 225 FloatBuffer vBuff = createFloatBuffer(3 * vertices); 226 return vBuff; 227 } 228 229 /** 230 * Create a new FloatBuffer of an appropriate size to hold the specified 231 * number of Vector3f object data only if the given buffer if not already 232 * the right size. 233 * 234 * @param buf 235 * the buffer to first check and rewind 236 * @param vertices 237 * number of vertices that need to be held by the newly created 238 * buffer 239 * @return the requested new FloatBuffer 240 */ createVector3Buffer(FloatBuffer buf, int vertices)241 public static FloatBuffer createVector3Buffer(FloatBuffer buf, int vertices) { 242 if (buf != null && buf.limit() == 3 * vertices) { 243 buf.rewind(); 244 return buf; 245 } 246 247 return createFloatBuffer(3 * vertices); 248 } 249 250 /** 251 * Sets the data contained in the given color into the FloatBuffer at the 252 * specified index. 253 * 254 * @param color 255 * the data to insert 256 * @param buf 257 * the buffer to insert into 258 * @param index 259 * the postion to place the data; in terms of colors not floats 260 */ setInBuffer(ColorRGBA color, FloatBuffer buf, int index)261 public static void setInBuffer(ColorRGBA color, FloatBuffer buf, 262 int index) { 263 buf.position(index * 4); 264 buf.put(color.r); 265 buf.put(color.g); 266 buf.put(color.b); 267 buf.put(color.a); 268 } 269 270 /** 271 * Sets the data contained in the given quaternion into the FloatBuffer at the 272 * specified index. 273 * 274 * @param quat 275 * the {@link Quaternion} to insert 276 * @param buf 277 * the buffer to insert into 278 * @param index 279 * the postion to place the data; in terms of quaternions not floats 280 */ setInBuffer(Quaternion quat, FloatBuffer buf, int index)281 public static void setInBuffer(Quaternion quat, FloatBuffer buf, 282 int index) { 283 buf.position(index * 4); 284 buf.put(quat.getX()); 285 buf.put(quat.getY()); 286 buf.put(quat.getZ()); 287 buf.put(quat.getW()); 288 } 289 290 /** 291 * Sets the data contained in the given Vector3F into the FloatBuffer at the 292 * specified index. 293 * 294 * @param vector 295 * the data to insert 296 * @param buf 297 * the buffer to insert into 298 * @param index 299 * the postion to place the data; in terms of vectors not floats 300 */ setInBuffer(Vector3f vector, FloatBuffer buf, int index)301 public static void setInBuffer(Vector3f vector, FloatBuffer buf, int index) { 302 if (buf == null) { 303 return; 304 } 305 if (vector == null) { 306 buf.put(index * 3, 0); 307 buf.put((index * 3) + 1, 0); 308 buf.put((index * 3) + 2, 0); 309 } else { 310 buf.put(index * 3, vector.x); 311 buf.put((index * 3) + 1, vector.y); 312 buf.put((index * 3) + 2, vector.z); 313 } 314 } 315 316 /** 317 * Updates the values of the given vector from the specified buffer at the 318 * index provided. 319 * 320 * @param vector 321 * the vector to set data on 322 * @param buf 323 * the buffer to read from 324 * @param index 325 * the position (in terms of vectors, not floats) to read from 326 * the buf 327 */ populateFromBuffer(Vector3f vector, FloatBuffer buf, int index)328 public static void populateFromBuffer(Vector3f vector, FloatBuffer buf, int index) { 329 vector.x = buf.get(index * 3); 330 vector.y = buf.get(index * 3 + 1); 331 vector.z = buf.get(index * 3 + 2); 332 } 333 334 /** 335 * Generates a Vector3f array from the given FloatBuffer. 336 * 337 * @param buff 338 * the FloatBuffer to read from 339 * @return a newly generated array of Vector3f objects 340 */ getVector3Array(FloatBuffer buff)341 public static Vector3f[] getVector3Array(FloatBuffer buff) { 342 buff.clear(); 343 Vector3f[] verts = new Vector3f[buff.limit() / 3]; 344 for (int x = 0; x < verts.length; x++) { 345 Vector3f v = new Vector3f(buff.get(), buff.get(), buff.get()); 346 verts[x] = v; 347 } 348 return verts; 349 } 350 351 /** 352 * Copies a Vector3f from one position in the buffer to another. The index 353 * values are in terms of vector number (eg, vector number 0 is postions 0-2 354 * in the FloatBuffer.) 355 * 356 * @param buf 357 * the buffer to copy from/to 358 * @param fromPos 359 * the index of the vector to copy 360 * @param toPos 361 * the index to copy the vector to 362 */ copyInternalVector3(FloatBuffer buf, int fromPos, int toPos)363 public static void copyInternalVector3(FloatBuffer buf, int fromPos, int toPos) { 364 copyInternal(buf, fromPos * 3, toPos * 3, 3); 365 } 366 367 /** 368 * Normalize a Vector3f in-buffer. 369 * 370 * @param buf 371 * the buffer to find the Vector3f within 372 * @param index 373 * the position (in terms of vectors, not floats) of the vector 374 * to normalize 375 */ normalizeVector3(FloatBuffer buf, int index)376 public static void normalizeVector3(FloatBuffer buf, int index) { 377 TempVars vars = TempVars.get(); 378 Vector3f tempVec3 = vars.vect1; 379 populateFromBuffer(tempVec3, buf, index); 380 tempVec3.normalizeLocal(); 381 setInBuffer(tempVec3, buf, index); 382 vars.release(); 383 } 384 385 /** 386 * Add to a Vector3f in-buffer. 387 * 388 * @param toAdd 389 * the vector to add from 390 * @param buf 391 * the buffer to find the Vector3f within 392 * @param index 393 * the position (in terms of vectors, not floats) of the vector 394 * to add to 395 */ addInBuffer(Vector3f toAdd, FloatBuffer buf, int index)396 public static void addInBuffer(Vector3f toAdd, FloatBuffer buf, int index) { 397 TempVars vars = TempVars.get(); 398 Vector3f tempVec3 = vars.vect1; 399 populateFromBuffer(tempVec3, buf, index); 400 tempVec3.addLocal(toAdd); 401 setInBuffer(tempVec3, buf, index); 402 vars.release(); 403 } 404 405 /** 406 * Multiply and store a Vector3f in-buffer. 407 * 408 * @param toMult 409 * the vector to multiply against 410 * @param buf 411 * the buffer to find the Vector3f within 412 * @param index 413 * the position (in terms of vectors, not floats) of the vector 414 * to multiply 415 */ multInBuffer(Vector3f toMult, FloatBuffer buf, int index)416 public static void multInBuffer(Vector3f toMult, FloatBuffer buf, int index) { 417 TempVars vars = TempVars.get(); 418 Vector3f tempVec3 = vars.vect1; 419 populateFromBuffer(tempVec3, buf, index); 420 tempVec3.multLocal(toMult); 421 setInBuffer(tempVec3, buf, index); 422 vars.release(); 423 } 424 425 /** 426 * Checks to see if the given Vector3f is equals to the data stored in the 427 * buffer at the given data index. 428 * 429 * @param check 430 * the vector to check against - null will return false. 431 * @param buf 432 * the buffer to compare data with 433 * @param index 434 * the position (in terms of vectors, not floats) of the vector 435 * in the buffer to check against 436 * @return 437 */ equals(Vector3f check, FloatBuffer buf, int index)438 public static boolean equals(Vector3f check, FloatBuffer buf, int index) { 439 TempVars vars = TempVars.get(); 440 Vector3f tempVec3 = vars.vect1; 441 populateFromBuffer(tempVec3, buf, index); 442 boolean eq = tempVec3.equals(check); 443 vars.release(); 444 return eq; 445 } 446 447 // // -- VECTOR2F METHODS -- //// 448 /** 449 * Generate a new FloatBuffer using the given array of Vector2f objects. 450 * The FloatBuffer will be 2 * data.length long and contain the vector data 451 * as data[0].x, data[0].y, data[1].x... etc. 452 * 453 * @param data array of Vector2f objects to place into a new FloatBuffer 454 */ createFloatBuffer(Vector2f... data)455 public static FloatBuffer createFloatBuffer(Vector2f... data) { 456 if (data == null) { 457 return null; 458 } 459 FloatBuffer buff = createFloatBuffer(2 * data.length); 460 for (int x = 0; x < data.length; x++) { 461 if (data[x] != null) { 462 buff.put(data[x].x).put(data[x].y); 463 } else { 464 buff.put(0).put(0); 465 } 466 } 467 buff.flip(); 468 return buff; 469 } 470 471 /** 472 * Create a new FloatBuffer of an appropriate size to hold the specified 473 * number of Vector2f object data. 474 * 475 * @param vertices 476 * number of vertices that need to be held by the newly created 477 * buffer 478 * @return the requested new FloatBuffer 479 */ createVector2Buffer(int vertices)480 public static FloatBuffer createVector2Buffer(int vertices) { 481 FloatBuffer vBuff = createFloatBuffer(2 * vertices); 482 return vBuff; 483 } 484 485 /** 486 * Create a new FloatBuffer of an appropriate size to hold the specified 487 * number of Vector2f object data only if the given buffer if not already 488 * the right size. 489 * 490 * @param buf 491 * the buffer to first check and rewind 492 * @param vertices 493 * number of vertices that need to be held by the newly created 494 * buffer 495 * @return the requested new FloatBuffer 496 */ createVector2Buffer(FloatBuffer buf, int vertices)497 public static FloatBuffer createVector2Buffer(FloatBuffer buf, int vertices) { 498 if (buf != null && buf.limit() == 2 * vertices) { 499 buf.rewind(); 500 return buf; 501 } 502 503 return createFloatBuffer(2 * vertices); 504 } 505 506 /** 507 * Sets the data contained in the given Vector2F into the FloatBuffer at the 508 * specified index. 509 * 510 * @param vector 511 * the data to insert 512 * @param buf 513 * the buffer to insert into 514 * @param index 515 * the postion to place the data; in terms of vectors not floats 516 */ setInBuffer(Vector2f vector, FloatBuffer buf, int index)517 public static void setInBuffer(Vector2f vector, FloatBuffer buf, int index) { 518 buf.put(index * 2, vector.x); 519 buf.put((index * 2) + 1, vector.y); 520 } 521 522 /** 523 * Updates the values of the given vector from the specified buffer at the 524 * index provided. 525 * 526 * @param vector 527 * the vector to set data on 528 * @param buf 529 * the buffer to read from 530 * @param index 531 * the position (in terms of vectors, not floats) to read from 532 * the buf 533 */ populateFromBuffer(Vector2f vector, FloatBuffer buf, int index)534 public static void populateFromBuffer(Vector2f vector, FloatBuffer buf, int index) { 535 vector.x = buf.get(index * 2); 536 vector.y = buf.get(index * 2 + 1); 537 } 538 539 /** 540 * Generates a Vector2f array from the given FloatBuffer. 541 * 542 * @param buff 543 * the FloatBuffer to read from 544 * @return a newly generated array of Vector2f objects 545 */ getVector2Array(FloatBuffer buff)546 public static Vector2f[] getVector2Array(FloatBuffer buff) { 547 buff.clear(); 548 Vector2f[] verts = new Vector2f[buff.limit() / 2]; 549 for (int x = 0; x < verts.length; x++) { 550 Vector2f v = new Vector2f(buff.get(), buff.get()); 551 verts[x] = v; 552 } 553 return verts; 554 } 555 556 /** 557 * Copies a Vector2f from one position in the buffer to another. The index 558 * values are in terms of vector number (eg, vector number 0 is postions 0-1 559 * in the FloatBuffer.) 560 * 561 * @param buf 562 * the buffer to copy from/to 563 * @param fromPos 564 * the index of the vector to copy 565 * @param toPos 566 * the index to copy the vector to 567 */ copyInternalVector2(FloatBuffer buf, int fromPos, int toPos)568 public static void copyInternalVector2(FloatBuffer buf, int fromPos, int toPos) { 569 copyInternal(buf, fromPos * 2, toPos * 2, 2); 570 } 571 572 /** 573 * Normalize a Vector2f in-buffer. 574 * 575 * @param buf 576 * the buffer to find the Vector2f within 577 * @param index 578 * the position (in terms of vectors, not floats) of the vector 579 * to normalize 580 */ normalizeVector2(FloatBuffer buf, int index)581 public static void normalizeVector2(FloatBuffer buf, int index) { 582 TempVars vars = TempVars.get(); 583 Vector2f tempVec2 = vars.vect2d; 584 populateFromBuffer(tempVec2, buf, index); 585 tempVec2.normalizeLocal(); 586 setInBuffer(tempVec2, buf, index); 587 vars.release(); 588 } 589 590 /** 591 * Add to a Vector2f in-buffer. 592 * 593 * @param toAdd 594 * the vector to add from 595 * @param buf 596 * the buffer to find the Vector2f within 597 * @param index 598 * the position (in terms of vectors, not floats) of the vector 599 * to add to 600 */ addInBuffer(Vector2f toAdd, FloatBuffer buf, int index)601 public static void addInBuffer(Vector2f toAdd, FloatBuffer buf, int index) { 602 TempVars vars = TempVars.get(); 603 Vector2f tempVec2 = vars.vect2d; 604 populateFromBuffer(tempVec2, buf, index); 605 tempVec2.addLocal(toAdd); 606 setInBuffer(tempVec2, buf, index); 607 vars.release(); 608 } 609 610 /** 611 * Multiply and store a Vector2f in-buffer. 612 * 613 * @param toMult 614 * the vector to multiply against 615 * @param buf 616 * the buffer to find the Vector2f within 617 * @param index 618 * the position (in terms of vectors, not floats) of the vector 619 * to multiply 620 */ multInBuffer(Vector2f toMult, FloatBuffer buf, int index)621 public static void multInBuffer(Vector2f toMult, FloatBuffer buf, int index) { 622 TempVars vars = TempVars.get(); 623 Vector2f tempVec2 = vars.vect2d; 624 populateFromBuffer(tempVec2, buf, index); 625 tempVec2.multLocal(toMult); 626 setInBuffer(tempVec2, buf, index); 627 vars.release(); 628 } 629 630 /** 631 * Checks to see if the given Vector2f is equals to the data stored in the 632 * buffer at the given data index. 633 * 634 * @param check 635 * the vector to check against - null will return false. 636 * @param buf 637 * the buffer to compare data with 638 * @param index 639 * the position (in terms of vectors, not floats) of the vector 640 * in the buffer to check against 641 * @return 642 */ equals(Vector2f check, FloatBuffer buf, int index)643 public static boolean equals(Vector2f check, FloatBuffer buf, int index) { 644 TempVars vars = TempVars.get(); 645 Vector2f tempVec2 = vars.vect2d; 646 populateFromBuffer(tempVec2, buf, index); 647 boolean eq = tempVec2.equals(check); 648 vars.release(); 649 return eq; 650 } 651 652 //// -- INT METHODS -- //// 653 /** 654 * Generate a new IntBuffer using the given array of ints. The IntBuffer 655 * will be data.length long and contain the int data as data[0], data[1]... 656 * etc. 657 * 658 * @param data 659 * array of ints to place into a new IntBuffer 660 */ createIntBuffer(int... data)661 public static IntBuffer createIntBuffer(int... data) { 662 if (data == null) { 663 return null; 664 } 665 IntBuffer buff = createIntBuffer(data.length); 666 buff.clear(); 667 buff.put(data); 668 buff.flip(); 669 return buff; 670 } 671 672 /** 673 * Create a new int[] array and populate it with the given IntBuffer's 674 * contents. 675 * 676 * @param buff 677 * the IntBuffer to read from 678 * @return a new int array populated from the IntBuffer 679 */ getIntArray(IntBuffer buff)680 public static int[] getIntArray(IntBuffer buff) { 681 if (buff == null) { 682 return null; 683 } 684 buff.clear(); 685 int[] inds = new int[buff.limit()]; 686 for (int x = 0; x < inds.length; x++) { 687 inds[x] = buff.get(); 688 } 689 return inds; 690 } 691 692 /** 693 * Create a new float[] array and populate it with the given FloatBuffer's 694 * contents. 695 * 696 * @param buff 697 * the FloatBuffer to read from 698 * @return a new float array populated from the FloatBuffer 699 */ getFloatArray(FloatBuffer buff)700 public static float[] getFloatArray(FloatBuffer buff) { 701 if (buff == null) { 702 return null; 703 } 704 buff.clear(); 705 float[] inds = new float[buff.limit()]; 706 for (int x = 0; x < inds.length; x++) { 707 inds[x] = buff.get(); 708 } 709 return inds; 710 } 711 712 //// -- GENERAL DOUBLE ROUTINES -- //// 713 /** 714 * Create a new DoubleBuffer of the specified size. 715 * 716 * @param size 717 * required number of double to store. 718 * @return the new DoubleBuffer 719 */ createDoubleBuffer(int size)720 public static DoubleBuffer createDoubleBuffer(int size) { 721 DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer(); 722 buf.clear(); 723 onBufferAllocated(buf); 724 return buf; 725 } 726 727 /** 728 * Create a new DoubleBuffer of an appropriate size to hold the specified 729 * number of doubles only if the given buffer if not already the right size. 730 * 731 * @param buf 732 * the buffer to first check and rewind 733 * @param size 734 * number of doubles that need to be held by the newly created 735 * buffer 736 * @return the requested new DoubleBuffer 737 */ createDoubleBuffer(DoubleBuffer buf, int size)738 public static DoubleBuffer createDoubleBuffer(DoubleBuffer buf, int size) { 739 if (buf != null && buf.limit() == size) { 740 buf.rewind(); 741 return buf; 742 } 743 744 buf = createDoubleBuffer(size); 745 return buf; 746 } 747 748 /** 749 * Creates a new DoubleBuffer with the same contents as the given 750 * DoubleBuffer. The new DoubleBuffer is seperate from the old one and 751 * changes are not reflected across. If you want to reflect changes, 752 * consider using Buffer.duplicate(). 753 * 754 * @param buf 755 * the DoubleBuffer to copy 756 * @return the copy 757 */ clone(DoubleBuffer buf)758 public static DoubleBuffer clone(DoubleBuffer buf) { 759 if (buf == null) { 760 return null; 761 } 762 buf.rewind(); 763 764 DoubleBuffer copy; 765 if (buf.isDirect()) { 766 copy = createDoubleBuffer(buf.limit()); 767 } else { 768 copy = DoubleBuffer.allocate(buf.limit()); 769 } 770 copy.put(buf); 771 772 return copy; 773 } 774 775 //// -- GENERAL FLOAT ROUTINES -- //// 776 /** 777 * Create a new FloatBuffer of the specified size. 778 * 779 * @param size 780 * required number of floats to store. 781 * @return the new FloatBuffer 782 */ createFloatBuffer(int size)783 public static FloatBuffer createFloatBuffer(int size) { 784 FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer(); 785 buf.clear(); 786 onBufferAllocated(buf); 787 return buf; 788 } 789 790 /** 791 * Copies floats from one position in the buffer to another. 792 * 793 * @param buf 794 * the buffer to copy from/to 795 * @param fromPos 796 * the starting point to copy from 797 * @param toPos 798 * the starting point to copy to 799 * @param length 800 * the number of floats to copy 801 */ copyInternal(FloatBuffer buf, int fromPos, int toPos, int length)802 public static void copyInternal(FloatBuffer buf, int fromPos, int toPos, int length) { 803 float[] data = new float[length]; 804 buf.position(fromPos); 805 buf.get(data); 806 buf.position(toPos); 807 buf.put(data); 808 } 809 810 /** 811 * Creates a new FloatBuffer with the same contents as the given 812 * FloatBuffer. The new FloatBuffer is seperate from the old one and changes 813 * are not reflected across. If you want to reflect changes, consider using 814 * Buffer.duplicate(). 815 * 816 * @param buf 817 * the FloatBuffer to copy 818 * @return the copy 819 */ clone(FloatBuffer buf)820 public static FloatBuffer clone(FloatBuffer buf) { 821 if (buf == null) { 822 return null; 823 } 824 buf.rewind(); 825 826 FloatBuffer copy; 827 if (buf.isDirect()) { 828 copy = createFloatBuffer(buf.limit()); 829 } else { 830 copy = FloatBuffer.allocate(buf.limit()); 831 } 832 copy.put(buf); 833 834 return copy; 835 } 836 837 //// -- GENERAL INT ROUTINES -- //// 838 /** 839 * Create a new IntBuffer of the specified size. 840 * 841 * @param size 842 * required number of ints to store. 843 * @return the new IntBuffer 844 */ createIntBuffer(int size)845 public static IntBuffer createIntBuffer(int size) { 846 IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer(); 847 buf.clear(); 848 onBufferAllocated(buf); 849 return buf; 850 } 851 852 /** 853 * Create a new IntBuffer of an appropriate size to hold the specified 854 * number of ints only if the given buffer if not already the right size. 855 * 856 * @param buf 857 * the buffer to first check and rewind 858 * @param size 859 * number of ints that need to be held by the newly created 860 * buffer 861 * @return the requested new IntBuffer 862 */ createIntBuffer(IntBuffer buf, int size)863 public static IntBuffer createIntBuffer(IntBuffer buf, int size) { 864 if (buf != null && buf.limit() == size) { 865 buf.rewind(); 866 return buf; 867 } 868 869 buf = createIntBuffer(size); 870 return buf; 871 } 872 873 /** 874 * Creates a new IntBuffer with the same contents as the given IntBuffer. 875 * The new IntBuffer is seperate from the old one and changes are not 876 * reflected across. If you want to reflect changes, consider using 877 * Buffer.duplicate(). 878 * 879 * @param buf 880 * the IntBuffer to copy 881 * @return the copy 882 */ clone(IntBuffer buf)883 public static IntBuffer clone(IntBuffer buf) { 884 if (buf == null) { 885 return null; 886 } 887 buf.rewind(); 888 889 IntBuffer copy; 890 if (buf.isDirect()) { 891 copy = createIntBuffer(buf.limit()); 892 } else { 893 copy = IntBuffer.allocate(buf.limit()); 894 } 895 copy.put(buf); 896 897 return copy; 898 } 899 900 //// -- GENERAL BYTE ROUTINES -- //// 901 /** 902 * Create a new ByteBuffer of the specified size. 903 * 904 * @param size 905 * required number of ints to store. 906 * @return the new IntBuffer 907 */ createByteBuffer(int size)908 public static ByteBuffer createByteBuffer(int size) { 909 ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); 910 buf.clear(); 911 onBufferAllocated(buf); 912 return buf; 913 } 914 915 /** 916 * Create a new ByteBuffer of an appropriate size to hold the specified 917 * number of ints only if the given buffer if not already the right size. 918 * 919 * @param buf 920 * the buffer to first check and rewind 921 * @param size 922 * number of bytes that need to be held by the newly created 923 * buffer 924 * @return the requested new IntBuffer 925 */ createByteBuffer(ByteBuffer buf, int size)926 public static ByteBuffer createByteBuffer(ByteBuffer buf, int size) { 927 if (buf != null && buf.limit() == size) { 928 buf.rewind(); 929 return buf; 930 } 931 932 buf = createByteBuffer(size); 933 return buf; 934 } 935 createByteBuffer(byte... data)936 public static ByteBuffer createByteBuffer(byte... data) { 937 ByteBuffer bb = createByteBuffer(data.length); 938 bb.put(data); 939 bb.flip(); 940 return bb; 941 } 942 createByteBuffer(String data)943 public static ByteBuffer createByteBuffer(String data) { 944 byte[] bytes = data.getBytes(); 945 ByteBuffer bb = createByteBuffer(bytes.length); 946 bb.put(bytes); 947 bb.flip(); 948 return bb; 949 } 950 951 /** 952 * Creates a new ByteBuffer with the same contents as the given ByteBuffer. 953 * The new ByteBuffer is seperate from the old one and changes are not 954 * reflected across. If you want to reflect changes, consider using 955 * Buffer.duplicate(). 956 * 957 * @param buf 958 * the ByteBuffer to copy 959 * @return the copy 960 */ clone(ByteBuffer buf)961 public static ByteBuffer clone(ByteBuffer buf) { 962 if (buf == null) { 963 return null; 964 } 965 buf.rewind(); 966 967 ByteBuffer copy; 968 if (buf.isDirect()) { 969 copy = createByteBuffer(buf.limit()); 970 } else { 971 copy = ByteBuffer.allocate(buf.limit()); 972 } 973 copy.put(buf); 974 975 return copy; 976 } 977 978 //// -- GENERAL SHORT ROUTINES -- //// 979 /** 980 * Create a new ShortBuffer of the specified size. 981 * 982 * @param size 983 * required number of shorts to store. 984 * @return the new ShortBuffer 985 */ createShortBuffer(int size)986 public static ShortBuffer createShortBuffer(int size) { 987 ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer(); 988 buf.clear(); 989 onBufferAllocated(buf); 990 return buf; 991 } 992 993 /** 994 * Create a new ShortBuffer of an appropriate size to hold the specified 995 * number of shorts only if the given buffer if not already the right size. 996 * 997 * @param buf 998 * the buffer to first check and rewind 999 * @param size 1000 * number of shorts that need to be held by the newly created 1001 * buffer 1002 * @return the requested new ShortBuffer 1003 */ createShortBuffer(ShortBuffer buf, int size)1004 public static ShortBuffer createShortBuffer(ShortBuffer buf, int size) { 1005 if (buf != null && buf.limit() == size) { 1006 buf.rewind(); 1007 return buf; 1008 } 1009 1010 buf = createShortBuffer(size); 1011 return buf; 1012 } 1013 createShortBuffer(short... data)1014 public static ShortBuffer createShortBuffer(short... data) { 1015 if (data == null) { 1016 return null; 1017 } 1018 ShortBuffer buff = createShortBuffer(data.length); 1019 buff.clear(); 1020 buff.put(data); 1021 buff.flip(); 1022 return buff; 1023 } 1024 1025 /** 1026 * Creates a new ShortBuffer with the same contents as the given ShortBuffer. 1027 * The new ShortBuffer is seperate from the old one and changes are not 1028 * reflected across. If you want to reflect changes, consider using 1029 * Buffer.duplicate(). 1030 * 1031 * @param buf 1032 * the ShortBuffer to copy 1033 * @return the copy 1034 */ clone(ShortBuffer buf)1035 public static ShortBuffer clone(ShortBuffer buf) { 1036 if (buf == null) { 1037 return null; 1038 } 1039 buf.rewind(); 1040 1041 ShortBuffer copy; 1042 if (buf.isDirect()) { 1043 copy = createShortBuffer(buf.limit()); 1044 } else { 1045 copy = ShortBuffer.allocate(buf.limit()); 1046 } 1047 copy.put(buf); 1048 1049 return copy; 1050 } 1051 1052 /** 1053 * Ensures there is at least the <code>required</code> number of entries left after the current position of the 1054 * buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer. 1055 * @param buffer buffer that should be checked/copied (may be null) 1056 * @param required minimum number of elements that should be remaining in the returned buffer 1057 * @return a buffer large enough to receive at least the <code>required</code> number of entries, same position as 1058 * the input buffer, not null 1059 */ ensureLargeEnough(FloatBuffer buffer, int required)1060 public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) { 1061 if (buffer == null || (buffer.remaining() < required)) { 1062 int position = (buffer != null ? buffer.position() : 0); 1063 FloatBuffer newVerts = createFloatBuffer(position + required); 1064 if (buffer != null) { 1065 buffer.rewind(); 1066 newVerts.put(buffer); 1067 newVerts.position(position); 1068 } 1069 buffer = newVerts; 1070 } 1071 return buffer; 1072 } 1073 ensureLargeEnough(ShortBuffer buffer, int required)1074 public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) { 1075 if (buffer == null || (buffer.remaining() < required)) { 1076 int position = (buffer != null ? buffer.position() : 0); 1077 ShortBuffer newVerts = createShortBuffer(position + required); 1078 if (buffer != null) { 1079 buffer.rewind(); 1080 newVerts.put(buffer); 1081 newVerts.position(position); 1082 } 1083 buffer = newVerts; 1084 } 1085 return buffer; 1086 } 1087 ensureLargeEnough(ByteBuffer buffer, int required)1088 public static ByteBuffer ensureLargeEnough(ByteBuffer buffer, int required) { 1089 if (buffer == null || (buffer.remaining() < required)) { 1090 int position = (buffer != null ? buffer.position() : 0); 1091 ByteBuffer newVerts = createByteBuffer(position + required); 1092 if (buffer != null) { 1093 buffer.rewind(); 1094 newVerts.put(buffer); 1095 newVerts.position(position); 1096 } 1097 buffer = newVerts; 1098 } 1099 return buffer; 1100 } 1101 printCurrentDirectMemory(StringBuilder store)1102 public static void printCurrentDirectMemory(StringBuilder store) { 1103 long totalHeld = 0; 1104 // make a new set to hold the keys to prevent concurrency issues. 1105 ArrayList<Buffer> bufs = new ArrayList<Buffer>(trackingHash.keySet()); 1106 int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0; 1107 int fBufsM = 0, bBufsM = 0, iBufsM = 0, sBufsM = 0, dBufsM = 0; 1108 for (Buffer b : bufs) { 1109 if (b instanceof ByteBuffer) { 1110 totalHeld += b.capacity(); 1111 bBufsM += b.capacity(); 1112 bBufs++; 1113 } else if (b instanceof FloatBuffer) { 1114 totalHeld += b.capacity() * 4; 1115 fBufsM += b.capacity() * 4; 1116 fBufs++; 1117 } else if (b instanceof IntBuffer) { 1118 totalHeld += b.capacity() * 4; 1119 iBufsM += b.capacity() * 4; 1120 iBufs++; 1121 } else if (b instanceof ShortBuffer) { 1122 totalHeld += b.capacity() * 2; 1123 sBufsM += b.capacity() * 2; 1124 sBufs++; 1125 } else if (b instanceof DoubleBuffer) { 1126 totalHeld += b.capacity() * 8; 1127 dBufsM += b.capacity() * 8; 1128 dBufs++; 1129 } 1130 } 1131 long heapMem = Runtime.getRuntime().totalMemory() 1132 - Runtime.getRuntime().freeMemory(); 1133 1134 boolean printStout = store == null; 1135 if (store == null) { 1136 store = new StringBuilder(); 1137 } 1138 store.append("Existing buffers: ").append(bufs.size()).append("\n"); 1139 store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs).append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n"); 1140 store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n"); 1141 store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n"); 1142 store.append("(b: ").append(bBufsM / 1024).append("kb f: ").append(fBufsM / 1024).append("kb i: ").append(iBufsM / 1024).append("kb s: ").append(sBufsM / 1024).append("kb d: ").append(dBufsM / 1024).append("kb)").append("\n"); 1143 if (printStout) { 1144 System.out.println(store.toString()); 1145 } 1146 } 1147 1148 private static final AtomicBoolean loadedMethods = new AtomicBoolean(false); 1149 private static Method cleanerMethod = null; 1150 private static Method cleanMethod = null; 1151 private static Method viewedBufferMethod = null; 1152 private static Method freeMethod = null; 1153 loadMethod(String className, String methodName)1154 private static Method loadMethod(String className, String methodName){ 1155 try { 1156 Method method = Class.forName(className).getMethod(methodName); 1157 method.setAccessible(true); 1158 return method; 1159 } catch (NoSuchMethodException ex) { 1160 return null; // the method was not found 1161 } catch (SecurityException ex) { 1162 return null; // setAccessible not allowed by security policy 1163 } catch (ClassNotFoundException ex) { 1164 return null; // the direct buffer implementation was not found 1165 } 1166 } 1167 loadCleanerMethods()1168 private static void loadCleanerMethods() { 1169 // If its already true, exit, if not, set it to true. 1170 if (loadedMethods.getAndSet(true)) { 1171 return; 1172 } 1173 // This could potentially be called many times if used from multiple 1174 // threads 1175 synchronized (loadedMethods) { 1176 // Oracle JRE / OpenJDK 1177 cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner"); 1178 cleanMethod = loadMethod("sun.misc.Cleaner", "clean"); 1179 viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer"); 1180 1181 // Apache Harmony 1182 freeMethod = loadMethod("org.apache.harmony.nio.internal.DirectBuffer", "free"); 1183 1184 // GUN Classpath (not likely) 1185 //finalizeMethod = loadMethod("java.nio.DirectByteBufferImpl", "finalize"); 1186 } 1187 } 1188 1189 /** 1190 * Direct buffers are garbage collected by using a phantom reference and a 1191 * reference queue. Every once a while, the JVM checks the reference queue and 1192 * cleans the direct buffers. However, as this doesn't happen 1193 * immediately after discarding all references to a direct buffer, it's 1194 * easy to OutOfMemoryError yourself using direct buffers. This function 1195 * explicitly calls the Cleaner method of a direct buffer. 1196 * 1197 * @param toBeDestroyed 1198 * The direct buffer that will be "cleaned". Utilizes reflection. 1199 * 1200 */ destroyDirectBuffer(Buffer toBeDestroyed)1201 public static void destroyDirectBuffer(Buffer toBeDestroyed) { 1202 if (!toBeDestroyed.isDirect()) { 1203 return; 1204 } 1205 1206 loadCleanerMethods(); 1207 1208 try { 1209 if (freeMethod != null) { 1210 freeMethod.invoke(toBeDestroyed); 1211 } else { 1212 Object cleaner = cleanerMethod.invoke(toBeDestroyed); 1213 if (cleaner != null) { 1214 cleanMethod.invoke(cleaner); 1215 } else { 1216 // Try the alternate approach of getting the viewed buffer first 1217 Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); 1218 if (viewedBuffer != null) { 1219 destroyDirectBuffer((Buffer) viewedBuffer); 1220 } else { 1221 Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed); 1222 } 1223 } 1224 } 1225 } catch (IllegalAccessException ex) { 1226 Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); 1227 } catch (IllegalArgumentException ex) { 1228 Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); 1229 } catch (InvocationTargetException ex) { 1230 Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); 1231 } catch (SecurityException ex) { 1232 Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); 1233 } 1234 } 1235 } 1236