-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.shader;
-
-import com.jme3.math.*;
-import com.jme3.util.BufferUtils;
-import java.nio.FloatBuffer;
-
-public class Uniform extends ShaderVariable {
-
- private static final Integer ZERO_INT = Integer.valueOf(0);
- private static final Float ZERO_FLT = Float.valueOf(0);
- private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(4*4);
-
- /**
- * Currently set value of the uniform.
- */
- protected Object value = null;
-
- /**
- * For arrays or matrices, efficient format
- * that can be sent to GL faster.
- */
- protected FloatBuffer multiData = null;
-
- /**
- * Type of uniform
- */
- protected VarType varType;
-
- /**
- * Binding to a renderer value, or null if user-defined uniform
- */
- protected UniformBinding binding;
-
- /**
- * Used to track which uniforms to clear to avoid
- * values leaking from other materials that use that shader.
- */
- protected boolean setByCurrentMaterial = false;
-
- @Override
- public String toString(){
- StringBuilder sb = new StringBuilder();
- sb.append("Uniform[name=");
- sb.append(name);
- if (varType != null){
- sb.append(", type=");
- sb.append(varType);
- sb.append(", value=");
- sb.append(value);
- }else{
- sb.append(", value=<not set>");
- }
- sb.append("]");
- return sb.toString();
- }
-
- public void setBinding(UniformBinding binding){
- this.binding = binding;
- }
-
- public UniformBinding getBinding(){
- return binding;
- }
-
- public VarType getVarType() {
- return varType;
- }
-
- public Object getValue(){
- return value;
- }
-
- public boolean isSetByCurrentMaterial() {
- return setByCurrentMaterial;
- }
-
- public void clearSetByCurrentMaterial(){
- setByCurrentMaterial = false;
- }
-
- private static void setVector4(Vector4f vec, Object value) {
- if (value instanceof ColorRGBA) {
- ColorRGBA color = (ColorRGBA) value;
- vec.set(color.r, color.g, color.b, color.a);
- } else if (value instanceof Quaternion) {
- Quaternion quat = (Quaternion) value;
- vec.set(quat.getX(), quat.getY(), quat.getZ(), quat.getW());
- } else if (value instanceof Vector4f) {
- Vector4f vec4 = (Vector4f) value;
- vec.set(vec4);
- } else{
- throw new IllegalArgumentException();
- }
- }
-
- public void clearValue(){
- updateNeeded = true;
-
- if (multiData != null){
- ZERO_BUF.clear();
- multiData.clear();
-
- while (multiData.remaining() > 0){
- ZERO_BUF.limit( Math.min(multiData.remaining(), 16) );
- multiData.put(ZERO_BUF);
- }
-
- multiData.clear();
-
- return;
- }
-
- if (varType == null) {
- return;
- }
-
- switch (varType){
- case Int:
- this.value = ZERO_INT;
- break;
- case Boolean:
- this.value = Boolean.FALSE;
- break;
- case Float:
- this.value = ZERO_FLT;
- break;
- case Vector2:
- this.value = Vector2f.ZERO;
- break;
- case Vector3:
- this.value = Vector3f.ZERO;
- break;
- case Vector4:
- this.value = Vector4f.ZERO;
- break;
- default:
- // won't happen because those are either textures
- // or multidata types
- }
- }
-
- public void setValue(VarType type, Object value){
- if (location == LOC_NOT_DEFINED) {
- return;
- }
-
- if (varType != null && varType != type) {
- throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
- }
-
- if (value == null) {
- throw new NullPointerException();
- }
-
- setByCurrentMaterial = true;
-
- switch (type){
- case Matrix3:
- Matrix3f m3 = (Matrix3f) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(9);
- }
- m3.fillFloatBuffer(multiData, true);
- multiData.clear();
- break;
- case Matrix4:
- Matrix4f m4 = (Matrix4f) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(16);
- }
- m4.fillFloatBuffer(multiData, true);
- multiData.clear();
- break;
- case FloatArray:
- float[] fa = (float[]) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(fa);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);
- }
- multiData.put(fa);
- multiData.clear();
- break;
- case Vector2Array:
- Vector2f[] v2a = (Vector2f[]) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(v2a);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);
- }
- for (int i = 0; i < v2a.length; i++) {
- BufferUtils.setInBuffer(v2a[i], multiData, i);
- }
- multiData.clear();
- break;
- case Vector3Array:
- Vector3f[] v3a = (Vector3f[]) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(v3a);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);
- }
- for (int i = 0; i < v3a.length; i++) {
- BufferUtils.setInBuffer(v3a[i], multiData, i);
- }
- multiData.clear();
- break;
- case Vector4Array:
- Vector4f[] v4a = (Vector4f[]) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(v4a);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);
- }
- for (int i = 0; i < v4a.length; i++) {
- BufferUtils.setInBuffer(v4a[i], multiData, i);
- }
- multiData.clear();
- break;
- case Matrix3Array:
- Matrix3f[] m3a = (Matrix3f[]) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(m3a.length * 9);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);
- }
- for (int i = 0; i < m3a.length; i++) {
- m3a[i].fillFloatBuffer(multiData, true);
- }
- multiData.clear();
- break;
- case Matrix4Array:
- Matrix4f[] m4a = (Matrix4f[]) value;
- if (multiData == null) {
- multiData = BufferUtils.createFloatBuffer(m4a.length * 16);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);
- }
- for (int i = 0; i < m4a.length; i++) {
- m4a[i].fillFloatBuffer(multiData, true);
- }
- multiData.clear();
- break;
- // Only use check if equals optimization for primitive values
- case Int:
- case Float:
- case Boolean:
- if (this.value != null && this.value.equals(value)) {
- return;
- }
- this.value = value;
- break;
- default:
- this.value = value;
- break;
- }
-
- if (multiData != null) {
- this.value = multiData;
- }
-
- varType = type;
- updateNeeded = true;
- }
-
- public void setVector4Length(int length){
- if (location == -1)
- return;
-
- FloatBuffer fb = (FloatBuffer) value;
- if (fb == null || fb.capacity() < length) {
- value = BufferUtils.createFloatBuffer(length * 4);
- }
-
- varType = VarType.Vector4Array;
- updateNeeded = true;
- setByCurrentMaterial = true;
- }
-
- public void setVector4InArray(float x, float y, float z, float w, int index){
- if (location == -1)
- return;
-
- if (varType != null && varType != VarType.Vector4Array)
- throw new IllegalArgumentException("Expected a "+varType.name()+" value!");
-
- FloatBuffer fb = (FloatBuffer) value;
- fb.position(index * 4);
- fb.put(x).put(y).put(z).put(w);
- fb.rewind();
- updateNeeded = true;
- setByCurrentMaterial = true;
- }
-
- public boolean isUpdateNeeded(){
- return updateNeeded;
- }
-
- public void clearUpdateNeeded(){
- updateNeeded = false;
- }
-
- public void reset(){
- setByCurrentMaterial = false;
- location = -2;
- updateNeeded = true;
- }
-
-}
+/*\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+ *\r
+ * * Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * * Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors\r
+ * may be used to endorse or promote products derived from this software\r
+ * without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\r
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+package com.jme3.shader;\r
+\r
+import com.jme3.math.*;\r
+import com.jme3.util.BufferUtils;\r
+import java.nio.FloatBuffer;\r
+import java.nio.IntBuffer;\r
+\r
+public class Uniform extends ShaderVariable {\r
+\r
+ private static final Integer ZERO_INT = Integer.valueOf(0);\r
+ private static final Float ZERO_FLT = Float.valueOf(0);\r
+ private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(4*4);\r
+\r
+ /**\r
+ * Currently set value of the uniform.\r
+ */\r
+ protected Object value = null;\r
+ \r
+ /**\r
+ * For arrays or matrices, efficient format\r
+ * that can be sent to GL faster.\r
+ */\r
+ protected FloatBuffer multiData = null;\r
+\r
+ /**\r
+ * Type of uniform\r
+ */\r
+ protected VarType varType;\r
+\r
+ /**\r
+ * Binding to a renderer value, or null if user-defined uniform\r
+ */\r
+ protected UniformBinding binding;\r
+\r
+ /**\r
+ * Used to track which uniforms to clear to avoid\r
+ * values leaking from other materials that use that shader.\r
+ */\r
+ protected boolean setByCurrentMaterial = false;\r
+\r
+ @Override\r
+ public String toString(){\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append("Uniform[name=");\r
+ sb.append(name);\r
+ if (varType != null){\r
+ sb.append(", type=");\r
+ sb.append(varType);\r
+ sb.append(", value=");\r
+ sb.append(value);\r
+ }else{\r
+ sb.append(", value=<not set>");\r
+ }\r
+ sb.append("]");\r
+ return sb.toString();\r
+ }\r
+\r
+ public void setBinding(UniformBinding binding){\r
+ this.binding = binding;\r
+ }\r
+\r
+ public UniformBinding getBinding(){\r
+ return binding;\r
+ }\r
+\r
+ public VarType getVarType() {\r
+ return varType;\r
+ }\r
+\r
+ public Object getValue(){\r
+ return value;\r
+ }\r
+\r
+ public boolean isSetByCurrentMaterial() {\r
+ return setByCurrentMaterial;\r
+ }\r
+\r
+ public void clearSetByCurrentMaterial(){\r
+ setByCurrentMaterial = false;\r
+ }\r
+\r
+ private static void setVector4(Vector4f vec, Object value) {\r
+ if (value instanceof ColorRGBA) {\r
+ ColorRGBA color = (ColorRGBA) value;\r
+ vec.set(color.r, color.g, color.b, color.a);\r
+ } else if (value instanceof Quaternion) {\r
+ Quaternion quat = (Quaternion) value;\r
+ vec.set(quat.getX(), quat.getY(), quat.getZ(), quat.getW());\r
+ } else if (value instanceof Vector4f) {\r
+ Vector4f vec4 = (Vector4f) value;\r
+ vec.set(vec4);\r
+ } else{\r
+ throw new IllegalArgumentException();\r
+ }\r
+ }\r
+ \r
+ public void clearValue(){\r
+ updateNeeded = true;\r
+\r
+ if (multiData != null){ \r
+ multiData.clear();\r
+\r
+ while (multiData.remaining() > 0){\r
+ ZERO_BUF.clear();\r
+ ZERO_BUF.limit( Math.min(multiData.remaining(), 16) );\r
+ multiData.put(ZERO_BUF);\r
+ }\r
+\r
+ multiData.clear();\r
+\r
+ return;\r
+ }\r
+\r
+ if (varType == null) {\r
+ return;\r
+ }\r
+ \r
+ switch (varType){\r
+ case Int:\r
+ this.value = ZERO_INT;\r
+ break;\r
+ case Boolean:\r
+ this.value = Boolean.FALSE;\r
+ break;\r
+ case Float:\r
+ this.value = ZERO_FLT; \r
+ break;\r
+ case Vector2:\r
+ this.value = Vector2f.ZERO;\r
+ break;\r
+ case Vector3:\r
+ this.value = Vector3f.ZERO;\r
+ break;\r
+ case Vector4:\r
+ this.value = Vector4f.ZERO;\r
+ break;\r
+ default:\r
+ // won't happen because those are either textures\r
+ // or multidata types\r
+ }\r
+ }\r
+ \r
+ public void setValue(VarType type, Object value){\r
+ if (location == LOC_NOT_DEFINED) {\r
+ return;\r
+ }\r
+\r
+ if (varType != null && varType != type) {\r
+ throw new IllegalArgumentException("Expected a " + varType.name() + " value!");\r
+ }\r
+\r
+ if (value == null) {\r
+ throw new NullPointerException();\r
+ }\r
+\r
+ setByCurrentMaterial = true;\r
+\r
+ switch (type){\r
+ case Matrix3:\r
+ Matrix3f m3 = (Matrix3f) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(9);\r
+ }\r
+ m3.fillFloatBuffer(multiData, true);\r
+ multiData.clear();\r
+ break;\r
+ case Matrix4:\r
+ Matrix4f m4 = (Matrix4f) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(16);\r
+ }\r
+ m4.fillFloatBuffer(multiData, true);\r
+ multiData.clear();\r
+ break;\r
+ case IntArray:\r
+ int[] ia = (int[]) value;\r
+ if (this.value == null) {\r
+ this.value = BufferUtils.createIntBuffer(ia);\r
+ } else {\r
+ this.value = BufferUtils.ensureLargeEnough((IntBuffer)this.value, ia.length);\r
+ }\r
+ ((IntBuffer)this.value).clear();\r
+ break;\r
+ case FloatArray:\r
+ float[] fa = (float[]) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(fa);\r
+ } else {\r
+ multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);\r
+ }\r
+ multiData.put(fa);\r
+ multiData.clear();\r
+ break;\r
+ case Vector2Array:\r
+ Vector2f[] v2a = (Vector2f[]) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(v2a);\r
+ } else {\r
+ multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);\r
+ }\r
+ for (int i = 0; i < v2a.length; i++) {\r
+ BufferUtils.setInBuffer(v2a[i], multiData, i);\r
+ }\r
+ multiData.clear();\r
+ break;\r
+ case Vector3Array:\r
+ Vector3f[] v3a = (Vector3f[]) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(v3a);\r
+ } else {\r
+ multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);\r
+ }\r
+ for (int i = 0; i < v3a.length; i++) {\r
+ BufferUtils.setInBuffer(v3a[i], multiData, i);\r
+ }\r
+ multiData.clear();\r
+ break;\r
+ case Vector4Array:\r
+ Vector4f[] v4a = (Vector4f[]) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(v4a);\r
+ } else {\r
+ multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);\r
+ }\r
+ for (int i = 0; i < v4a.length; i++) {\r
+ BufferUtils.setInBuffer(v4a[i], multiData, i);\r
+ }\r
+ multiData.clear();\r
+ break;\r
+ case Matrix3Array:\r
+ Matrix3f[] m3a = (Matrix3f[]) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(m3a.length * 9);\r
+ } else {\r
+ multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);\r
+ }\r
+ for (int i = 0; i < m3a.length; i++) {\r
+ m3a[i].fillFloatBuffer(multiData, true);\r
+ }\r
+ multiData.clear();\r
+ break;\r
+ case Matrix4Array:\r
+ Matrix4f[] m4a = (Matrix4f[]) value;\r
+ if (multiData == null) {\r
+ multiData = BufferUtils.createFloatBuffer(m4a.length * 16);\r
+ } else {\r
+ multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);\r
+ }\r
+ for (int i = 0; i < m4a.length; i++) {\r
+ m4a[i].fillFloatBuffer(multiData, true);\r
+ }\r
+ multiData.clear();\r
+ break;\r
+ // Only use check if equals optimization for primitive values\r
+ case Int:\r
+ case Float:\r
+ case Boolean:\r
+ if (this.value != null && this.value.equals(value)) {\r
+ return;\r
+ }\r
+ this.value = value;\r
+ break;\r
+ default:\r
+ this.value = value;\r
+ break;\r
+ }\r
+\r
+ if (multiData != null) {\r
+ this.value = multiData;\r
+ }\r
+\r
+ varType = type;\r
+ updateNeeded = true;\r
+ }\r
+\r
+ public void setVector4Length(int length){\r
+ if (location == -1)\r
+ return;\r
+\r
+ FloatBuffer fb = (FloatBuffer) value;\r
+ if (fb == null || fb.capacity() < length) {\r
+ value = BufferUtils.createFloatBuffer(length * 4);\r
+ }\r
+\r
+ varType = VarType.Vector4Array;\r
+ updateNeeded = true;\r
+ setByCurrentMaterial = true;\r
+ }\r
+\r
+ public void setVector4InArray(float x, float y, float z, float w, int index){\r
+ if (location == -1)\r
+ return;\r
+\r
+ if (varType != null && varType != VarType.Vector4Array)\r
+ throw new IllegalArgumentException("Expected a "+varType.name()+" value!");\r
+\r
+ FloatBuffer fb = (FloatBuffer) value;\r
+ fb.position(index * 4);\r
+ fb.put(x).put(y).put(z).put(w);\r
+ fb.rewind();\r
+ updateNeeded = true;\r
+ setByCurrentMaterial = true;\r
+ }\r
+ \r
+ public boolean isUpdateNeeded(){\r
+ return updateNeeded;\r
+ }\r
+\r
+ public void clearUpdateNeeded(){\r
+ updateNeeded = false;\r
+ }\r
+\r
+ public void reset(){\r
+ setByCurrentMaterial = false;\r
+ location = -2;\r
+ updateNeeded = true;\r
+ }\r
+\r
+}\r
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package com.jme3.util;
-
-import com.jme3.math.ColorRGBA;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.math.Vector4f;
-import java.lang.ref.PhantomReference;
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.DoubleBuffer;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.nio.LongBuffer;
-import java.nio.ShortBuffer;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * <code>BufferUtils</code> is a helper class for generating nio buffers from
- * jME data classes such as Vectors and ColorRGBA.
- *
- * @author Joshua Slack
- * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
- */
-public final class BufferUtils {
-
- private static boolean trackDirectMemory = false;
- private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>();
- private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>();
- static ClearReferences cleanupthread;
-
- /**
- * Set it to true if you want to enable direct memory tracking for debugging purpose.
- * Default is false.
- * To print direct memory usage use BufferUtils.printCurrentDirectMemory(StringBuilder store);
- * @param enabled
- */
- public static void setTrackDirectMemoryEnabled(boolean enabled) {
- trackDirectMemory = enabled;
- }
-
- /**
- * Creates a clone of the given buffer. The clone's capacity is
- * equal to the given buffer's limit.
- *
- * @param buf The buffer to clone
- * @return The cloned buffer
- */
- public static Buffer clone(Buffer buf) {
- if (buf instanceof FloatBuffer) {
- return clone((FloatBuffer) buf);
- } else if (buf instanceof ShortBuffer) {
- return clone((ShortBuffer) buf);
- } else if (buf instanceof ByteBuffer) {
- return clone((ByteBuffer) buf);
- } else if (buf instanceof IntBuffer) {
- return clone((IntBuffer) buf);
- } else if (buf instanceof DoubleBuffer) {
- return clone((DoubleBuffer) buf);
- } else {
- throw new UnsupportedOperationException();
- }
- }
-
- private static void onBufferAllocated(Buffer buffer) {
- /**
- * StackTraceElement[] stackTrace = new Throwable().getStackTrace(); int
- * initialIndex = 0;
- *
- * for (int i = 0; i < stackTrace.length; i++){ if
- * (!stackTrace[i].getClassName().equals(BufferUtils.class.getName())){
- * initialIndex = i; break; } }
- *
- * int allocated = buffer.capacity(); int size = 0;
- *
- * if (buffer instanceof FloatBuffer){ size = 4; }else if (buffer
- * instanceof ShortBuffer){ size = 2; }else if (buffer instanceof
- * ByteBuffer){ size = 1; }else if (buffer instanceof IntBuffer){ size =
- * 4; }else if (buffer instanceof DoubleBuffer){ size = 8; }
- *
- * allocated *= size;
- *
- * for (int i = initialIndex; i < stackTrace.length; i++){
- * StackTraceElement element = stackTrace[i]; if
- * (element.getClassName().startsWith("java")){ break; }
- *
- * try { Class clazz = Class.forName(element.getClassName()); if (i ==
- * initialIndex){
- * System.out.println(clazz.getSimpleName()+"."+element.getMethodName
- * ()+"():" + element.getLineNumber() + " allocated " + allocated);
- * }else{ System.out.println(" at " +
- * clazz.getSimpleName()+"."+element.getMethodName()+"()"); } } catch
- * (ClassNotFoundException ex) { } }
- */
- if (BufferUtils.trackDirectMemory) {
-
- if (BufferUtils.cleanupthread == null) {
- BufferUtils.cleanupthread = new ClearReferences();
- BufferUtils.cleanupthread.start();
- }
- if (buffer instanceof ByteBuffer) {
- BufferInfo info = new BufferInfo(ByteBuffer.class, buffer.capacity(), buffer, BufferUtils.removeCollected);
- BufferUtils.trackedBuffers.put(info, info);
- } else if (buffer instanceof FloatBuffer) {
- BufferInfo info = new BufferInfo(FloatBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
- BufferUtils.trackedBuffers.put(info, info);
- } else if (buffer instanceof IntBuffer) {
- BufferInfo info = new BufferInfo(IntBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);
- BufferUtils.trackedBuffers.put(info, info);
- } else if (buffer instanceof ShortBuffer) {
- BufferInfo info = new BufferInfo(ShortBuffer.class, buffer.capacity() * 2, buffer, BufferUtils.removeCollected);
- BufferUtils.trackedBuffers.put(info, info);
- } else if (buffer instanceof DoubleBuffer) {
- BufferInfo info = new BufferInfo(DoubleBuffer.class, buffer.capacity() * 8, buffer, BufferUtils.removeCollected);
- BufferUtils.trackedBuffers.put(info, info);
- }
-
- }
- }
-
- /**
- * Generate a new FloatBuffer using the given array of Vector3f objects.
- * The FloatBuffer will be 3 * data.length long and contain the vector data
- * as data[0].x, data[0].y, data[0].z, data[1].x... etc.
- *
- * @param data array of Vector3f objects to place into a new FloatBuffer
- */
- public static FloatBuffer createFloatBuffer(Vector3f... data) {
- if (data == null) {
- return null;
- }
- FloatBuffer buff = createFloatBuffer(3 * data.length);
- for (Vector3f element : data) {
- if (element != null) {
- buff.put(element.x).put(element.y).put(element.z);
- } else {
- buff.put(0).put(0).put(0);
- }
- }
- buff.flip();
- return buff;
- }
-
- /**
- * Generate a new FloatBuffer using the given array of Quaternion objects.
- * The FloatBuffer will be 4 * data.length long and contain the vector data.
- *
- * @param data array of Quaternion objects to place into a new FloatBuffer
- */
- public static FloatBuffer createFloatBuffer(Quaternion... data) {
- if (data == null) {
- return null;
- }
- FloatBuffer buff = createFloatBuffer(4 * data.length);
- for (Quaternion element : data) {
- if (element != null) {
- buff.put(element.getX()).put(element.getY()).put(element.getZ()).put(element.getW());
- } else {
- buff.put(0).put(0).put(0).put(0);
- }
- }
- buff.flip();
- return buff;
- }
-
- /**
- * Generate a new FloatBuffer using the given array of Vector4 objects.
- * The FloatBuffer will be 4 * data.length long and contain the vector data.
- *
- * @param data array of Vector4 objects to place into a new FloatBuffer
- */
- public static FloatBuffer createFloatBuffer(Vector4f... data) {
- if (data == null) {
- return null;
- }
- FloatBuffer buff = createFloatBuffer(4 * data.length);
- for (int x = 0; x < data.length; x++) {
- if (data[x] != null) {
- buff.put(data[x].getX()).put(data[x].getY()).put(data[x].getZ()).put(data[x].getW());
- } else {
- buff.put(0).put(0).put(0).put(0);
- }
- }
- buff.flip();
- return buff;
- }
-
- /**
- * Generate a new FloatBuffer using the given array of float primitives.
- * @param data array of float primitives to place into a new FloatBuffer
- */
- public static FloatBuffer createFloatBuffer(float... data) {
- if (data == null) {
- return null;
- }
- FloatBuffer buff = createFloatBuffer(data.length);
- buff.clear();
- buff.put(data);
- buff.flip();
- return buff;
- }
-
- /**
- * Create a new FloatBuffer of an appropriate size to hold the specified
- * number of Vector3f object data.
- *
- * @param vertices
- * number of vertices that need to be held by the newly created
- * buffer
- * @return the requested new FloatBuffer
- */
- public static FloatBuffer createVector3Buffer(int vertices) {
- FloatBuffer vBuff = createFloatBuffer(3 * vertices);
- return vBuff;
- }
-
- /**
- * Create a new FloatBuffer of an appropriate size to hold the specified
- * number of Vector3f object data only if the given buffer if not already
- * the right size.
- *
- * @param buf
- * the buffer to first check and rewind
- * @param vertices
- * number of vertices that need to be held by the newly created
- * buffer
- * @return the requested new FloatBuffer
- */
- public static FloatBuffer createVector3Buffer(FloatBuffer buf, int vertices) {
- if (buf != null && buf.limit() == 3 * vertices) {
- buf.rewind();
- return buf;
- }
-
- return createFloatBuffer(3 * vertices);
- }
-
- /**
- * Sets the data contained in the given color into the FloatBuffer at the
- * specified index.
- *
- * @param color
- * the data to insert
- * @param buf
- * the buffer to insert into
- * @param index
- * the postion to place the data; in terms of colors not floats
- */
- public static void setInBuffer(ColorRGBA color, FloatBuffer buf,
- int index) {
- buf.position(index * 4);
- buf.put(color.r);
- buf.put(color.g);
- buf.put(color.b);
- buf.put(color.a);
- }
-
- /**
- * Sets the data contained in the given quaternion into the FloatBuffer at the
- * specified index.
- *
- * @param quat
- * the {@link Quaternion} to insert
- * @param buf
- * the buffer to insert into
- * @param index
- * the postion to place the data; in terms of quaternions not floats
- */
- public static void setInBuffer(Quaternion quat, FloatBuffer buf,
- int index) {
- buf.position(index * 4);
- buf.put(quat.getX());
- buf.put(quat.getY());
- buf.put(quat.getZ());
- buf.put(quat.getW());
- }
-
- /**
- * Sets the data contained in the given vector4 into the FloatBuffer at the
- * specified index.
- *
- * @param vec
- * the {@link Vector4f} to insert
- * @param buf
- * the buffer to insert into
- * @param index
- * the postion to place the data; in terms of vector4 not floats
- */
- public static void setInBuffer(Vector4f vec, FloatBuffer buf,
- int index) {
- buf.position(index * 4);
- buf.put(vec.getX());
- buf.put(vec.getY());
- buf.put(vec.getZ());
- buf.put(vec.getW());
- }
-
- /**
- * Sets the data contained in the given Vector3F into the FloatBuffer at the
- * specified index.
- *
- * @param vector
- * the data to insert
- * @param buf
- * the buffer to insert into
- * @param index
- * the postion to place the data; in terms of vectors not floats
- */
- public static void setInBuffer(Vector3f vector, FloatBuffer buf, int index) {
- if (buf == null) {
- return;
- }
- if (vector == null) {
- buf.put(index * 3, 0);
- buf.put((index * 3) + 1, 0);
- buf.put((index * 3) + 2, 0);
- } else {
- buf.put(index * 3, vector.x);
- buf.put((index * 3) + 1, vector.y);
- buf.put((index * 3) + 2, vector.z);
- }
- }
-
- /**
- * Updates the values of the given vector from the specified buffer at the
- * index provided.
- *
- * @param vector
- * the vector to set data on
- * @param buf
- * the buffer to read from
- * @param index
- * the position (in terms of vectors, not floats) to read from
- * the buf
- */
- public static void populateFromBuffer(Vector3f vector, FloatBuffer buf, int index) {
- vector.x = buf.get(index * 3);
- vector.y = buf.get(index * 3 + 1);
- vector.z = buf.get(index * 3 + 2);
- }
-
- /**
- * Generates a Vector3f array from the given FloatBuffer.
- *
- * @param buff
- * the FloatBuffer to read from
- * @return a newly generated array of Vector3f objects
- */
- public static Vector3f[] getVector3Array(FloatBuffer buff) {
- buff.clear();
- Vector3f[] verts = new Vector3f[buff.limit() / 3];
- for (int x = 0; x < verts.length; x++) {
- Vector3f v = new Vector3f(buff.get(), buff.get(), buff.get());
- verts[x] = v;
- }
- return verts;
- }
-
- /**
- * Copies a Vector3f from one position in the buffer to another. The index
- * values are in terms of vector number (eg, vector number 0 is postions 0-2
- * in the FloatBuffer.)
- *
- * @param buf
- * the buffer to copy from/to
- * @param fromPos
- * the index of the vector to copy
- * @param toPos
- * the index to copy the vector to
- */
- public static void copyInternalVector3(FloatBuffer buf, int fromPos, int toPos) {
- copyInternal(buf, fromPos * 3, toPos * 3, 3);
- }
-
- /**
- * Normalize a Vector3f in-buffer.
- *
- * @param buf
- * the buffer to find the Vector3f within
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * to normalize
- */
- public static void normalizeVector3(FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector3f tempVec3 = vars.vect1;
- populateFromBuffer(tempVec3, buf, index);
- tempVec3.normalizeLocal();
- setInBuffer(tempVec3, buf, index);
- vars.release();
- }
-
- /**
- * Add to a Vector3f in-buffer.
- *
- * @param toAdd
- * the vector to add from
- * @param buf
- * the buffer to find the Vector3f within
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * to add to
- */
- public static void addInBuffer(Vector3f toAdd, FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector3f tempVec3 = vars.vect1;
- populateFromBuffer(tempVec3, buf, index);
- tempVec3.addLocal(toAdd);
- setInBuffer(tempVec3, buf, index);
- vars.release();
- }
-
- /**
- * Multiply and store a Vector3f in-buffer.
- *
- * @param toMult
- * the vector to multiply against
- * @param buf
- * the buffer to find the Vector3f within
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * to multiply
- */
- public static void multInBuffer(Vector3f toMult, FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector3f tempVec3 = vars.vect1;
- populateFromBuffer(tempVec3, buf, index);
- tempVec3.multLocal(toMult);
- setInBuffer(tempVec3, buf, index);
- vars.release();
- }
-
- /**
- * Checks to see if the given Vector3f is equals to the data stored in the
- * buffer at the given data index.
- *
- * @param check
- * the vector to check against - null will return false.
- * @param buf
- * the buffer to compare data with
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * in the buffer to check against
- * @return true if the data is equivalent, otherwise false.
- */
- public static boolean equals(Vector3f check, FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector3f tempVec3 = vars.vect1;
- populateFromBuffer(tempVec3, buf, index);
- boolean eq = tempVec3.equals(check);
- vars.release();
- return eq;
- }
-
- // // -- VECTOR2F METHODS -- ////
- /**
- * Generate a new FloatBuffer using the given array of Vector2f objects.
- * The FloatBuffer will be 2 * data.length long and contain the vector data
- * as data[0].x, data[0].y, data[1].x... etc.
- *
- * @param data array of Vector2f objects to place into a new FloatBuffer
- */
- public static FloatBuffer createFloatBuffer(Vector2f... data) {
- if (data == null) {
- return null;
- }
- FloatBuffer buff = createFloatBuffer(2 * data.length);
- for (Vector2f element : data) {
- if (element != null) {
- buff.put(element.x).put(element.y);
- } else {
- buff.put(0).put(0);
- }
- }
- buff.flip();
- return buff;
- }
-
- /**
- * Create a new FloatBuffer of an appropriate size to hold the specified
- * number of Vector2f object data.
- *
- * @param vertices
- * number of vertices that need to be held by the newly created
- * buffer
- * @return the requested new FloatBuffer
- */
- public static FloatBuffer createVector2Buffer(int vertices) {
- FloatBuffer vBuff = createFloatBuffer(2 * vertices);
- return vBuff;
- }
-
- /**
- * Create a new FloatBuffer of an appropriate size to hold the specified
- * number of Vector2f object data only if the given buffer if not already
- * the right size.
- *
- * @param buf
- * the buffer to first check and rewind
- * @param vertices
- * number of vertices that need to be held by the newly created
- * buffer
- * @return the requested new FloatBuffer
- */
- public static FloatBuffer createVector2Buffer(FloatBuffer buf, int vertices) {
- if (buf != null && buf.limit() == 2 * vertices) {
- buf.rewind();
- return buf;
- }
-
- return createFloatBuffer(2 * vertices);
- }
-
- /**
- * Sets the data contained in the given Vector2F into the FloatBuffer at the
- * specified index.
- *
- * @param vector
- * the data to insert
- * @param buf
- * the buffer to insert into
- * @param index
- * the postion to place the data; in terms of vectors not floats
- */
- public static void setInBuffer(Vector2f vector, FloatBuffer buf, int index) {
- buf.put(index * 2, vector.x);
- buf.put((index * 2) + 1, vector.y);
- }
-
- /**
- * Updates the values of the given vector from the specified buffer at the
- * index provided.
- *
- * @param vector
- * the vector to set data on
- * @param buf
- * the buffer to read from
- * @param index
- * the position (in terms of vectors, not floats) to read from
- * the buf
- */
- public static void populateFromBuffer(Vector2f vector, FloatBuffer buf, int index) {
- vector.x = buf.get(index * 2);
- vector.y = buf.get(index * 2 + 1);
- }
-
- /**
- * Generates a Vector2f array from the given FloatBuffer.
- *
- * @param buff
- * the FloatBuffer to read from
- * @return a newly generated array of Vector2f objects
- */
- public static Vector2f[] getVector2Array(FloatBuffer buff) {
- buff.clear();
- Vector2f[] verts = new Vector2f[buff.limit() / 2];
- for (int x = 0; x < verts.length; x++) {
- Vector2f v = new Vector2f(buff.get(), buff.get());
- verts[x] = v;
- }
- return verts;
- }
-
- /**
- * Copies a Vector2f from one position in the buffer to another. The index
- * values are in terms of vector number (eg, vector number 0 is postions 0-1
- * in the FloatBuffer.)
- *
- * @param buf
- * the buffer to copy from/to
- * @param fromPos
- * the index of the vector to copy
- * @param toPos
- * the index to copy the vector to
- */
- public static void copyInternalVector2(FloatBuffer buf, int fromPos, int toPos) {
- copyInternal(buf, fromPos * 2, toPos * 2, 2);
- }
-
- /**
- * Normalize a Vector2f in-buffer.
- *
- * @param buf
- * the buffer to find the Vector2f within
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * to normalize
- */
- public static void normalizeVector2(FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector2f tempVec2 = vars.vect2d;
- populateFromBuffer(tempVec2, buf, index);
- tempVec2.normalizeLocal();
- setInBuffer(tempVec2, buf, index);
- vars.release();
- }
-
- /**
- * Add to a Vector2f in-buffer.
- *
- * @param toAdd
- * the vector to add from
- * @param buf
- * the buffer to find the Vector2f within
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * to add to
- */
- public static void addInBuffer(Vector2f toAdd, FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector2f tempVec2 = vars.vect2d;
- populateFromBuffer(tempVec2, buf, index);
- tempVec2.addLocal(toAdd);
- setInBuffer(tempVec2, buf, index);
- vars.release();
- }
-
- /**
- * Multiply and store a Vector2f in-buffer.
- *
- * @param toMult
- * the vector to multiply against
- * @param buf
- * the buffer to find the Vector2f within
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * to multiply
- */
- public static void multInBuffer(Vector2f toMult, FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector2f tempVec2 = vars.vect2d;
- populateFromBuffer(tempVec2, buf, index);
- tempVec2.multLocal(toMult);
- setInBuffer(tempVec2, buf, index);
- vars.release();
- }
-
- /**
- * Checks to see if the given Vector2f is equals to the data stored in the
- * buffer at the given data index.
- *
- * @param check
- * the vector to check against - null will return false.
- * @param buf
- * the buffer to compare data with
- * @param index
- * the position (in terms of vectors, not floats) of the vector
- * in the buffer to check against
- * @return true if the data is equivalent, otherwise false.
- */
- public static boolean equals(Vector2f check, FloatBuffer buf, int index) {
- TempVars vars = TempVars.get();
- Vector2f tempVec2 = vars.vect2d;
- populateFromBuffer(tempVec2, buf, index);
- boolean eq = tempVec2.equals(check);
- vars.release();
- return eq;
- }
-
- //// -- INT METHODS -- ////
- /**
- * Generate a new IntBuffer using the given array of ints. The IntBuffer
- * will be data.length long and contain the int data as data[0], data[1]...
- * etc.
- *
- * @param data
- * array of ints to place into a new IntBuffer
- */
- public static IntBuffer createIntBuffer(int... data) {
- if (data == null) {
- return null;
- }
- IntBuffer buff = createIntBuffer(data.length);
- buff.clear();
- buff.put(data);
- buff.flip();
- return buff;
- }
-
- /**
- * Create a new int[] array and populate it with the given IntBuffer's
- * contents.
- *
- * @param buff
- * the IntBuffer to read from
- * @return a new int array populated from the IntBuffer
- */
- public static int[] getIntArray(IntBuffer buff) {
- if (buff == null) {
- return null;
- }
- buff.clear();
- int[] inds = new int[buff.limit()];
- for (int x = 0; x < inds.length; x++) {
- inds[x] = buff.get();
- }
- return inds;
- }
-
- /**
- * Create a new float[] array and populate it with the given FloatBuffer's
- * contents.
- *
- * @param buff
- * the FloatBuffer to read from
- * @return a new float array populated from the FloatBuffer
- */
- public static float[] getFloatArray(FloatBuffer buff) {
- if (buff == null) {
- return null;
- }
- buff.clear();
- float[] inds = new float[buff.limit()];
- for (int x = 0; x < inds.length; x++) {
- inds[x] = buff.get();
- }
- return inds;
- }
-
- //// -- GENERAL DOUBLE ROUTINES -- ////
- /**
- * Create a new DoubleBuffer of the specified size.
- *
- * @param size
- * required number of double to store.
- * @return the new DoubleBuffer
- */
- public static DoubleBuffer createDoubleBuffer(int size) {
- DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();
- buf.clear();
- onBufferAllocated(buf);
- return buf;
- }
-
- /**
- * Create a new DoubleBuffer of an appropriate size to hold the specified
- * number of doubles only if the given buffer if not already the right size.
- *
- * @param buf
- * the buffer to first check and rewind
- * @param size
- * number of doubles that need to be held by the newly created
- * buffer
- * @return the requested new DoubleBuffer
- */
- public static DoubleBuffer createDoubleBuffer(DoubleBuffer buf, int size) {
- if (buf != null && buf.limit() == size) {
- buf.rewind();
- return buf;
- }
-
- buf = createDoubleBuffer(size);
- return buf;
- }
-
- /**
- * Creates a new DoubleBuffer with the same contents as the given
- * DoubleBuffer. The new DoubleBuffer is seperate from the old one and
- * changes are not reflected across. If you want to reflect changes,
- * consider using Buffer.duplicate().
- *
- * @param buf
- * the DoubleBuffer to copy
- * @return the copy
- */
- public static DoubleBuffer clone(DoubleBuffer buf) {
- if (buf == null) {
- return null;
- }
- buf.rewind();
-
- DoubleBuffer copy;
- if (isDirect(buf)) {
- copy = createDoubleBuffer(buf.limit());
- } else {
- copy = DoubleBuffer.allocate(buf.limit());
- }
- copy.put(buf);
-
- return copy;
- }
-
- //// -- GENERAL FLOAT ROUTINES -- ////
- /**
- * Create a new FloatBuffer of the specified size.
- *
- * @param size
- * required number of floats to store.
- * @return the new FloatBuffer
- */
- public static FloatBuffer createFloatBuffer(int size) {
- FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();
- buf.clear();
- onBufferAllocated(buf);
- return buf;
- }
-
- /**
- * Copies floats from one position in the buffer to another.
- *
- * @param buf
- * the buffer to copy from/to
- * @param fromPos
- * the starting point to copy from
- * @param toPos
- * the starting point to copy to
- * @param length
- * the number of floats to copy
- */
- public static void copyInternal(FloatBuffer buf, int fromPos, int toPos, int length) {
- float[] data = new float[length];
- buf.position(fromPos);
- buf.get(data);
- buf.position(toPos);
- buf.put(data);
- }
-
- /**
- * Creates a new FloatBuffer with the same contents as the given
- * FloatBuffer. The new FloatBuffer is seperate from the old one and changes
- * are not reflected across. If you want to reflect changes, consider using
- * Buffer.duplicate().
- *
- * @param buf
- * the FloatBuffer to copy
- * @return the copy
- */
- public static FloatBuffer clone(FloatBuffer buf) {
- if (buf == null) {
- return null;
- }
- buf.rewind();
-
- FloatBuffer copy;
- if (isDirect(buf)) {
- copy = createFloatBuffer(buf.limit());
- } else {
- copy = FloatBuffer.allocate(buf.limit());
- }
- copy.put(buf);
-
- return copy;
- }
-
- //// -- GENERAL INT ROUTINES -- ////
- /**
- * Create a new IntBuffer of the specified size.
- *
- * @param size
- * required number of ints to store.
- * @return the new IntBuffer
- */
- public static IntBuffer createIntBuffer(int size) {
- IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();
- buf.clear();
- onBufferAllocated(buf);
- return buf;
- }
-
- /**
- * Create a new IntBuffer of an appropriate size to hold the specified
- * number of ints only if the given buffer if not already the right size.
- *
- * @param buf
- * the buffer to first check and rewind
- * @param size
- * number of ints that need to be held by the newly created
- * buffer
- * @return the requested new IntBuffer
- */
- public static IntBuffer createIntBuffer(IntBuffer buf, int size) {
- if (buf != null && buf.limit() == size) {
- buf.rewind();
- return buf;
- }
-
- buf = createIntBuffer(size);
- return buf;
- }
-
- /**
- * Creates a new IntBuffer with the same contents as the given IntBuffer.
- * The new IntBuffer is seperate from the old one and changes are not
- * reflected across. If you want to reflect changes, consider using
- * Buffer.duplicate().
- *
- * @param buf
- * the IntBuffer to copy
- * @return the copy
- */
- public static IntBuffer clone(IntBuffer buf) {
- if (buf == null) {
- return null;
- }
- buf.rewind();
-
- IntBuffer copy;
- if (isDirect(buf)) {
- copy = createIntBuffer(buf.limit());
- } else {
- copy = IntBuffer.allocate(buf.limit());
- }
- copy.put(buf);
-
- return copy;
- }
-
- //// -- GENERAL BYTE ROUTINES -- ////
- /**
- * Create a new ByteBuffer of the specified size.
- *
- * @param size
- * required number of ints to store.
- * @return the new IntBuffer
- */
- public static ByteBuffer createByteBuffer(int size) {
- ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
- buf.clear();
- onBufferAllocated(buf);
- return buf;
- }
-
- /**
- * Create a new ByteBuffer of an appropriate size to hold the specified
- * number of ints only if the given buffer if not already the right size.
- *
- * @param buf
- * the buffer to first check and rewind
- * @param size
- * number of bytes that need to be held by the newly created
- * buffer
- * @return the requested new IntBuffer
- */
- public static ByteBuffer createByteBuffer(ByteBuffer buf, int size) {
- if (buf != null && buf.limit() == size) {
- buf.rewind();
- return buf;
- }
-
- buf = createByteBuffer(size);
- return buf;
- }
-
- public static ByteBuffer createByteBuffer(byte... data) {
- ByteBuffer bb = createByteBuffer(data.length);
- bb.put(data);
- bb.flip();
- return bb;
- }
-
- public static ByteBuffer createByteBuffer(String data) {
- byte[] bytes = data.getBytes();
- ByteBuffer bb = createByteBuffer(bytes.length);
- bb.put(bytes);
- bb.flip();
- return bb;
- }
-
- /**
- * Creates a new ByteBuffer with the same contents as the given ByteBuffer.
- * The new ByteBuffer is seperate from the old one and changes are not
- * reflected across. If you want to reflect changes, consider using
- * Buffer.duplicate().
- *
- * @param buf
- * the ByteBuffer to copy
- * @return the copy
- */
- public static ByteBuffer clone(ByteBuffer buf) {
- if (buf == null) {
- return null;
- }
- buf.rewind();
-
- ByteBuffer copy;
- if (isDirect(buf)) {
- copy = createByteBuffer(buf.limit());
- } else {
- copy = ByteBuffer.allocate(buf.limit());
- }
- copy.put(buf);
-
- return copy;
- }
-
- //// -- GENERAL SHORT ROUTINES -- ////
- /**
- * Create a new ShortBuffer of the specified size.
- *
- * @param size
- * required number of shorts to store.
- * @return the new ShortBuffer
- */
- public static ShortBuffer createShortBuffer(int size) {
- ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();
- buf.clear();
- onBufferAllocated(buf);
- return buf;
- }
-
- /**
- * Create a new ShortBuffer of an appropriate size to hold the specified
- * number of shorts only if the given buffer if not already the right size.
- *
- * @param buf
- * the buffer to first check and rewind
- * @param size
- * number of shorts that need to be held by the newly created
- * buffer
- * @return the requested new ShortBuffer
- */
- public static ShortBuffer createShortBuffer(ShortBuffer buf, int size) {
- if (buf != null && buf.limit() == size) {
- buf.rewind();
- return buf;
- }
-
- buf = createShortBuffer(size);
- return buf;
- }
-
- public static ShortBuffer createShortBuffer(short... data) {
- if (data == null) {
- return null;
- }
- ShortBuffer buff = createShortBuffer(data.length);
- buff.clear();
- buff.put(data);
- buff.flip();
- return buff;
- }
-
- /**
- * Creates a new ShortBuffer with the same contents as the given ShortBuffer.
- * The new ShortBuffer is seperate from the old one and changes are not
- * reflected across. If you want to reflect changes, consider using
- * Buffer.duplicate().
- *
- * @param buf
- * the ShortBuffer to copy
- * @return the copy
- */
- public static ShortBuffer clone(ShortBuffer buf) {
- if (buf == null) {
- return null;
- }
- buf.rewind();
-
- ShortBuffer copy;
- if (isDirect(buf)) {
- copy = createShortBuffer(buf.limit());
- } else {
- copy = ShortBuffer.allocate(buf.limit());
- }
- copy.put(buf);
-
- return copy;
- }
-
- /**
- * Ensures there is at least the <code>required</code> number of entries left after the current position of the
- * buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer.
- * @param buffer buffer that should be checked/copied (may be null)
- * @param required minimum number of elements that should be remaining in the returned buffer
- * @return a buffer large enough to receive at least the <code>required</code> number of entries, same position as
- * the input buffer, not null
- */
- public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {
- if (buffer != null) {
- buffer.limit(buffer.capacity());
- }
- if (buffer == null || (buffer.remaining() < required)) {
- int position = (buffer != null ? buffer.position() : 0);
- FloatBuffer newVerts = createFloatBuffer(position + required);
- if (buffer != null) {
- buffer.flip();
- newVerts.put(buffer);
- newVerts.position(position);
- }
- buffer = newVerts;
- }
- return buffer;
- }
-
- public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
- if (buffer != null) {
- buffer.limit(buffer.capacity());
- }
- if (buffer == null || (buffer.remaining() < required)) {
- int position = (buffer != null ? buffer.position() : 0);
- ShortBuffer newVerts = createShortBuffer(position + required);
- if (buffer != null) {
- buffer.flip();
- newVerts.put(buffer);
- newVerts.position(position);
- }
- buffer = newVerts;
- }
- return buffer;
- }
-
- public static ByteBuffer ensureLargeEnough(ByteBuffer buffer, int required) {
- if (buffer != null) {
- buffer.limit(buffer.capacity());
- }
- if (buffer == null || (buffer.remaining() < required)) {
- int position = (buffer != null ? buffer.position() : 0);
- ByteBuffer newVerts = createByteBuffer(position + required);
- if (buffer != null) {
- buffer.flip();
- newVerts.put(buffer);
- newVerts.position(position);
- }
- buffer = newVerts;
- }
- return buffer;
- }
-
- public static void printCurrentDirectMemory(StringBuilder store) {
- long totalHeld = 0;
- long heapMem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
-
- boolean printStout = store == null;
- if (store == null) {
- store = new StringBuilder();
- }
- if (trackDirectMemory) {
- // make a new set to hold the keys to prevent concurrency issues.
- int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0;
- int fBufsM = 0, bBufsM = 0, iBufsM = 0, sBufsM = 0, dBufsM = 0;
- for (BufferInfo b : BufferUtils.trackedBuffers.values()) {
- if (b.type == ByteBuffer.class) {
- totalHeld += b.size;
- bBufsM += b.size;
- bBufs++;
- } else if (b.type == FloatBuffer.class) {
- totalHeld += b.size;
- fBufsM += b.size;
- fBufs++;
- } else if (b.type == IntBuffer.class) {
- totalHeld += b.size;
- iBufsM += b.size;
- iBufs++;
- } else if (b.type == ShortBuffer.class) {
- totalHeld += b.size;
- sBufsM += b.size;
- sBufs++;
- } else if (b.type == DoubleBuffer.class) {
- totalHeld += b.size;
- dBufsM += b.size;
- dBufs++;
- }
- }
-
- store.append("Existing buffers: ").append(BufferUtils.trackedBuffers.size()).append("\n");
- store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs).append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n");
- store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");
- store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");
- 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");
- } else {
- store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");
- store.append("Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.").append("\n");
- }
- if (printStout) {
- System.out.println(store.toString());
- }
- }
- private static final AtomicBoolean loadedMethods = new AtomicBoolean(false);
- private static Method cleanerMethod = null;
- private static Method cleanMethod = null;
- private static Method viewedBufferMethod = null;
- private static Method freeMethod = null;
-
- private static Method loadMethod(String className, String methodName) {
- try {
- Method method = Class.forName(className).getMethod(methodName);
- method.setAccessible(true);
- return method;
- } catch (NoSuchMethodException ex) {
- return null; // the method was not found
- } catch (SecurityException ex) {
- return null; // setAccessible not allowed by security policy
- } catch (ClassNotFoundException ex) {
- return null; // the direct buffer implementation was not found
- }
- }
-
- private static void loadCleanerMethods() {
- // If its already true, exit, if not, set it to true.
- if (BufferUtils.loadedMethods.getAndSet(true)) {
- return;
- }
- // This could potentially be called many times if used from multiple
- // threads
- synchronized (BufferUtils.loadedMethods) {
- // Oracle JRE / OpenJDK
- cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");
- cleanMethod = loadMethod("sun.misc.Cleaner", "clean");
- viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");
- if (viewedBufferMethod == null) {
- // They changed the name in Java 7 (???)
- viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment");
- }
-
- // Apache Harmony
- ByteBuffer bb = BufferUtils.createByteBuffer(1);
- Class<?> clazz = bb.getClass();
- try {
- freeMethod = clazz.getMethod("free");
- } catch (NoSuchMethodException ex) {
- } catch (SecurityException ex) {
- }
- }
- }
-
- /**
- * Direct buffers are garbage collected by using a phantom reference and a
- * reference queue. Every once a while, the JVM checks the reference queue and
- * cleans the direct buffers. However, as this doesn't happen
- * immediately after discarding all references to a direct buffer, it's
- * easy to OutOfMemoryError yourself using direct buffers. This function
- * explicitly calls the Cleaner method of a direct buffer.
- *
- * @param toBeDestroyed
- * The direct buffer that will be "cleaned". Utilizes reflection.
- *
- */
- public static void destroyDirectBuffer(Buffer toBeDestroyed) {
- if (!isDirect(toBeDestroyed)) {
- return;
- }
-
- BufferUtils.loadCleanerMethods();
-
- try {
- if (freeMethod != null) {
- freeMethod.invoke(toBeDestroyed);
- } else {
- Object cleaner = cleanerMethod.invoke(toBeDestroyed);
- if (cleaner != null) {
- cleanMethod.invoke(cleaner);
- } else {
- // Try the alternate approach of getting the viewed buffer first
- Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);
- if (viewedBuffer != null) {
- destroyDirectBuffer((Buffer) viewedBuffer);
- } else {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed);
- }
- }
- }
- } catch (IllegalAccessException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- } catch (IllegalArgumentException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- } catch (InvocationTargetException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- } catch (SecurityException ex) {
- Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);
- }
- }
-
- /*
- * FIXME when java 1.5 supprt is dropped - replace calls to this method with Buffer.isDirect
- *
- * Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer subclasses :
- * FloatBuffer, IntBuffer, ShortBuffer, ByteBuffer,DoubleBuffer, LongBuffer.
- * CharBuffer has been excluded as we don't use it.
- *
- */
- private static boolean isDirect(Buffer buf) {
- if (buf instanceof FloatBuffer) {
- return ((FloatBuffer) buf).isDirect();
- }
- if (buf instanceof IntBuffer) {
- return ((IntBuffer) buf).isDirect();
- }
- if (buf instanceof ShortBuffer) {
- return ((ShortBuffer) buf).isDirect();
- }
- if (buf instanceof ByteBuffer) {
- return ((ByteBuffer) buf).isDirect();
- }
- if (buf instanceof DoubleBuffer) {
- return ((DoubleBuffer) buf).isDirect();
- }
- if (buf instanceof LongBuffer) {
- return ((LongBuffer) buf).isDirect();
- }
- throw new UnsupportedOperationException(" BufferUtils.isDirect was called on " + buf.getClass().getName());
- }
-
- private static class BufferInfo extends PhantomReference<Buffer> {
-
- private Class type;
- private int size;
-
- public BufferInfo(Class type, int size, Buffer referent, ReferenceQueue<? super Buffer> q) {
- super(referent, q);
- this.type = type;
- this.size = size;
- }
- }
-
- private static class ClearReferences extends Thread {
-
- ClearReferences() {
- this.setDaemon(true);
- }
-
- @Override
- public void run() {
- try {
- while (true) {
- Reference<? extends Buffer> toclean = BufferUtils.removeCollected.remove();
- BufferUtils.trackedBuffers.remove(toclean);
- }
-
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-}
\ No newline at end of file
+/*\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+ *\r
+ * * Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * * Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors\r
+ * may be used to endorse or promote products derived from this software\r
+ * without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\r
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+package com.jme3.util;\r
+\r
+import com.jme3.math.ColorRGBA;\r
+import com.jme3.math.Quaternion;\r
+import com.jme3.math.Vector2f;\r
+import com.jme3.math.Vector3f;\r
+import com.jme3.math.Vector4f;\r
+import java.lang.ref.PhantomReference;\r
+import java.lang.ref.Reference;\r
+import java.lang.ref.ReferenceQueue;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.lang.reflect.Method;\r
+import java.nio.Buffer;\r
+import java.nio.ByteBuffer;\r
+import java.nio.ByteOrder;\r
+import java.nio.DoubleBuffer;\r
+import java.nio.FloatBuffer;\r
+import java.nio.IntBuffer;\r
+import java.nio.LongBuffer;\r
+import java.nio.ShortBuffer;\r
+import java.util.concurrent.ConcurrentHashMap;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+/**\r
+ * <code>BufferUtils</code> is a helper class for generating nio buffers from\r
+ * jME data classes such as Vectors and ColorRGBA.\r
+ * \r
+ * @author Joshua Slack\r
+ * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $\r
+ */\r
+public final class BufferUtils {\r
+\r
+ private static boolean trackDirectMemory = false;\r
+ private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>();\r
+ private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>();\r
+ static ClearReferences cleanupthread;\r
+\r
+ /**\r
+ * Set it to true if you want to enable direct memory tracking for debugging purpose.\r
+ * Default is false.\r
+ * To print direct memory usage use BufferUtils.printCurrentDirectMemory(StringBuilder store);\r
+ * @param enabled \r
+ */\r
+ public static void setTrackDirectMemoryEnabled(boolean enabled) {\r
+ trackDirectMemory = enabled;\r
+ }\r
+\r
+ /**\r
+ * Creates a clone of the given buffer. The clone's capacity is \r
+ * equal to the given buffer's limit.\r
+ * \r
+ * @param buf The buffer to clone\r
+ * @return The cloned buffer\r
+ */\r
+ public static Buffer clone(Buffer buf) {\r
+ if (buf instanceof FloatBuffer) {\r
+ return clone((FloatBuffer) buf);\r
+ } else if (buf instanceof ShortBuffer) {\r
+ return clone((ShortBuffer) buf);\r
+ } else if (buf instanceof ByteBuffer) {\r
+ return clone((ByteBuffer) buf);\r
+ } else if (buf instanceof IntBuffer) {\r
+ return clone((IntBuffer) buf);\r
+ } else if (buf instanceof DoubleBuffer) {\r
+ return clone((DoubleBuffer) buf);\r
+ } else {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+ }\r
+\r
+ private static void onBufferAllocated(Buffer buffer) {\r
+ /**\r
+ * StackTraceElement[] stackTrace = new Throwable().getStackTrace(); int\r
+ * initialIndex = 0;\r
+ * \r
+ * for (int i = 0; i < stackTrace.length; i++){ if\r
+ * (!stackTrace[i].getClassName().equals(BufferUtils.class.getName())){\r
+ * initialIndex = i; break; } }\r
+ * \r
+ * int allocated = buffer.capacity(); int size = 0;\r
+ * \r
+ * if (buffer instanceof FloatBuffer){ size = 4; }else if (buffer\r
+ * instanceof ShortBuffer){ size = 2; }else if (buffer instanceof\r
+ * ByteBuffer){ size = 1; }else if (buffer instanceof IntBuffer){ size =\r
+ * 4; }else if (buffer instanceof DoubleBuffer){ size = 8; }\r
+ * \r
+ * allocated *= size;\r
+ * \r
+ * for (int i = initialIndex; i < stackTrace.length; i++){\r
+ * StackTraceElement element = stackTrace[i]; if\r
+ * (element.getClassName().startsWith("java")){ break; }\r
+ * \r
+ * try { Class clazz = Class.forName(element.getClassName()); if (i ==\r
+ * initialIndex){\r
+ * System.out.println(clazz.getSimpleName()+"."+element.getMethodName\r
+ * ()+"():" + element.getLineNumber() + " allocated " + allocated);\r
+ * }else{ System.out.println(" at " +\r
+ * clazz.getSimpleName()+"."+element.getMethodName()+"()"); } } catch\r
+ * (ClassNotFoundException ex) { } }\r
+ */\r
+ if (BufferUtils.trackDirectMemory) {\r
+\r
+ if (BufferUtils.cleanupthread == null) {\r
+ BufferUtils.cleanupthread = new ClearReferences();\r
+ BufferUtils.cleanupthread.start();\r
+ }\r
+ if (buffer instanceof ByteBuffer) {\r
+ BufferInfo info = new BufferInfo(ByteBuffer.class, buffer.capacity(), buffer, BufferUtils.removeCollected);\r
+ BufferUtils.trackedBuffers.put(info, info);\r
+ } else if (buffer instanceof FloatBuffer) {\r
+ BufferInfo info = new BufferInfo(FloatBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);\r
+ BufferUtils.trackedBuffers.put(info, info);\r
+ } else if (buffer instanceof IntBuffer) {\r
+ BufferInfo info = new BufferInfo(IntBuffer.class, buffer.capacity() * 4, buffer, BufferUtils.removeCollected);\r
+ BufferUtils.trackedBuffers.put(info, info);\r
+ } else if (buffer instanceof ShortBuffer) {\r
+ BufferInfo info = new BufferInfo(ShortBuffer.class, buffer.capacity() * 2, buffer, BufferUtils.removeCollected);\r
+ BufferUtils.trackedBuffers.put(info, info);\r
+ } else if (buffer instanceof DoubleBuffer) {\r
+ BufferInfo info = new BufferInfo(DoubleBuffer.class, buffer.capacity() * 8, buffer, BufferUtils.removeCollected);\r
+ BufferUtils.trackedBuffers.put(info, info);\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Generate a new FloatBuffer using the given array of Vector3f objects. \r
+ * The FloatBuffer will be 3 * data.length long and contain the vector data \r
+ * as data[0].x, data[0].y, data[0].z, data[1].x... etc.\r
+ * \r
+ * @param data array of Vector3f objects to place into a new FloatBuffer\r
+ */\r
+ public static FloatBuffer createFloatBuffer(Vector3f... data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ FloatBuffer buff = createFloatBuffer(3 * data.length);\r
+ for (Vector3f element : data) {\r
+ if (element != null) {\r
+ buff.put(element.x).put(element.y).put(element.z);\r
+ } else {\r
+ buff.put(0).put(0).put(0);\r
+ }\r
+ }\r
+ buff.flip();\r
+ return buff;\r
+ }\r
+\r
+ /**\r
+ * Generate a new FloatBuffer using the given array of Quaternion objects.\r
+ * The FloatBuffer will be 4 * data.length long and contain the vector data.\r
+ * \r
+ * @param data array of Quaternion objects to place into a new FloatBuffer\r
+ */\r
+ public static FloatBuffer createFloatBuffer(Quaternion... data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ FloatBuffer buff = createFloatBuffer(4 * data.length);\r
+ for (Quaternion element : data) {\r
+ if (element != null) {\r
+ buff.put(element.getX()).put(element.getY()).put(element.getZ()).put(element.getW());\r
+ } else {\r
+ buff.put(0).put(0).put(0).put(0);\r
+ }\r
+ }\r
+ buff.flip();\r
+ return buff;\r
+ }\r
+\r
+ /**\r
+ * Generate a new FloatBuffer using the given array of Vector4 objects.\r
+ * The FloatBuffer will be 4 * data.length long and contain the vector data.\r
+ *\r
+ * @param data array of Vector4 objects to place into a new FloatBuffer\r
+ */\r
+ public static FloatBuffer createFloatBuffer(Vector4f... data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ FloatBuffer buff = createFloatBuffer(4 * data.length);\r
+ for (int x = 0; x < data.length; x++) {\r
+ if (data[x] != null) {\r
+ buff.put(data[x].getX()).put(data[x].getY()).put(data[x].getZ()).put(data[x].getW());\r
+ } else {\r
+ buff.put(0).put(0).put(0).put(0);\r
+ }\r
+ }\r
+ buff.flip();\r
+ return buff;\r
+ }\r
+\r
+ /**\r
+ * Generate a new FloatBuffer using the given array of float primitives.\r
+ * @param data array of float primitives to place into a new FloatBuffer\r
+ */\r
+ public static FloatBuffer createFloatBuffer(float... data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ FloatBuffer buff = createFloatBuffer(data.length);\r
+ buff.clear();\r
+ buff.put(data);\r
+ buff.flip();\r
+ return buff;\r
+ }\r
+\r
+ /**\r
+ * Create a new FloatBuffer of an appropriate size to hold the specified\r
+ * number of Vector3f object data.\r
+ * \r
+ * @param vertices\r
+ * number of vertices that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new FloatBuffer\r
+ */\r
+ public static FloatBuffer createVector3Buffer(int vertices) {\r
+ FloatBuffer vBuff = createFloatBuffer(3 * vertices);\r
+ return vBuff;\r
+ }\r
+\r
+ /**\r
+ * Create a new FloatBuffer of an appropriate size to hold the specified\r
+ * number of Vector3f object data only if the given buffer if not already\r
+ * the right size.\r
+ * \r
+ * @param buf\r
+ * the buffer to first check and rewind\r
+ * @param vertices\r
+ * number of vertices that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new FloatBuffer\r
+ */\r
+ public static FloatBuffer createVector3Buffer(FloatBuffer buf, int vertices) {\r
+ if (buf != null && buf.limit() == 3 * vertices) {\r
+ buf.rewind();\r
+ return buf;\r
+ }\r
+\r
+ return createFloatBuffer(3 * vertices);\r
+ }\r
+\r
+ /**\r
+ * Sets the data contained in the given color into the FloatBuffer at the\r
+ * specified index.\r
+ * \r
+ * @param color\r
+ * the data to insert\r
+ * @param buf\r
+ * the buffer to insert into\r
+ * @param index\r
+ * the postion to place the data; in terms of colors not floats\r
+ */\r
+ public static void setInBuffer(ColorRGBA color, FloatBuffer buf,\r
+ int index) {\r
+ buf.position(index * 4);\r
+ buf.put(color.r);\r
+ buf.put(color.g);\r
+ buf.put(color.b);\r
+ buf.put(color.a);\r
+ }\r
+\r
+ /**\r
+ * Sets the data contained in the given quaternion into the FloatBuffer at the \r
+ * specified index.\r
+ * \r
+ * @param quat\r
+ * the {@link Quaternion} to insert\r
+ * @param buf\r
+ * the buffer to insert into\r
+ * @param index\r
+ * the postion to place the data; in terms of quaternions not floats\r
+ */\r
+ public static void setInBuffer(Quaternion quat, FloatBuffer buf,\r
+ int index) {\r
+ buf.position(index * 4);\r
+ buf.put(quat.getX());\r
+ buf.put(quat.getY());\r
+ buf.put(quat.getZ());\r
+ buf.put(quat.getW());\r
+ }\r
+\r
+ /**\r
+ * Sets the data contained in the given vector4 into the FloatBuffer at the\r
+ * specified index.\r
+ *\r
+ * @param vec\r
+ * the {@link Vector4f} to insert\r
+ * @param buf\r
+ * the buffer to insert into\r
+ * @param index\r
+ * the postion to place the data; in terms of vector4 not floats\r
+ */\r
+ public static void setInBuffer(Vector4f vec, FloatBuffer buf,\r
+ int index) {\r
+ buf.position(index * 4);\r
+ buf.put(vec.getX());\r
+ buf.put(vec.getY());\r
+ buf.put(vec.getZ());\r
+ buf.put(vec.getW());\r
+ }\r
+\r
+ /**\r
+ * Sets the data contained in the given Vector3F into the FloatBuffer at the\r
+ * specified index.\r
+ * \r
+ * @param vector\r
+ * the data to insert\r
+ * @param buf\r
+ * the buffer to insert into\r
+ * @param index\r
+ * the postion to place the data; in terms of vectors not floats\r
+ */\r
+ public static void setInBuffer(Vector3f vector, FloatBuffer buf, int index) {\r
+ if (buf == null) {\r
+ return;\r
+ }\r
+ if (vector == null) {\r
+ buf.put(index * 3, 0);\r
+ buf.put((index * 3) + 1, 0);\r
+ buf.put((index * 3) + 2, 0);\r
+ } else {\r
+ buf.put(index * 3, vector.x);\r
+ buf.put((index * 3) + 1, vector.y);\r
+ buf.put((index * 3) + 2, vector.z);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Updates the values of the given vector from the specified buffer at the\r
+ * index provided.\r
+ * \r
+ * @param vector\r
+ * the vector to set data on\r
+ * @param buf\r
+ * the buffer to read from\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) to read from\r
+ * the buf\r
+ */\r
+ public static void populateFromBuffer(Vector3f vector, FloatBuffer buf, int index) {\r
+ vector.x = buf.get(index * 3);\r
+ vector.y = buf.get(index * 3 + 1);\r
+ vector.z = buf.get(index * 3 + 2);\r
+ }\r
+\r
+ /**\r
+ * Generates a Vector3f array from the given FloatBuffer.\r
+ * \r
+ * @param buff\r
+ * the FloatBuffer to read from\r
+ * @return a newly generated array of Vector3f objects\r
+ */\r
+ public static Vector3f[] getVector3Array(FloatBuffer buff) {\r
+ buff.clear();\r
+ Vector3f[] verts = new Vector3f[buff.limit() / 3];\r
+ for (int x = 0; x < verts.length; x++) {\r
+ Vector3f v = new Vector3f(buff.get(), buff.get(), buff.get());\r
+ verts[x] = v;\r
+ }\r
+ return verts;\r
+ }\r
+\r
+ /**\r
+ * Copies a Vector3f from one position in the buffer to another. The index\r
+ * values are in terms of vector number (eg, vector number 0 is postions 0-2\r
+ * in the FloatBuffer.)\r
+ * \r
+ * @param buf\r
+ * the buffer to copy from/to\r
+ * @param fromPos\r
+ * the index of the vector to copy\r
+ * @param toPos\r
+ * the index to copy the vector to\r
+ */\r
+ public static void copyInternalVector3(FloatBuffer buf, int fromPos, int toPos) {\r
+ copyInternal(buf, fromPos * 3, toPos * 3, 3);\r
+ }\r
+\r
+ /**\r
+ * Normalize a Vector3f in-buffer.\r
+ * \r
+ * @param buf\r
+ * the buffer to find the Vector3f within\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * to normalize\r
+ */\r
+ public static void normalizeVector3(FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector3f tempVec3 = vars.vect1;\r
+ populateFromBuffer(tempVec3, buf, index);\r
+ tempVec3.normalizeLocal();\r
+ setInBuffer(tempVec3, buf, index);\r
+ vars.release();\r
+ }\r
+\r
+ /**\r
+ * Add to a Vector3f in-buffer.\r
+ * \r
+ * @param toAdd\r
+ * the vector to add from\r
+ * @param buf\r
+ * the buffer to find the Vector3f within\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * to add to\r
+ */\r
+ public static void addInBuffer(Vector3f toAdd, FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector3f tempVec3 = vars.vect1;\r
+ populateFromBuffer(tempVec3, buf, index);\r
+ tempVec3.addLocal(toAdd);\r
+ setInBuffer(tempVec3, buf, index);\r
+ vars.release();\r
+ }\r
+\r
+ /**\r
+ * Multiply and store a Vector3f in-buffer.\r
+ * \r
+ * @param toMult\r
+ * the vector to multiply against\r
+ * @param buf\r
+ * the buffer to find the Vector3f within\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * to multiply\r
+ */\r
+ public static void multInBuffer(Vector3f toMult, FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector3f tempVec3 = vars.vect1;\r
+ populateFromBuffer(tempVec3, buf, index);\r
+ tempVec3.multLocal(toMult);\r
+ setInBuffer(tempVec3, buf, index);\r
+ vars.release();\r
+ }\r
+\r
+ /**\r
+ * Checks to see if the given Vector3f is equals to the data stored in the\r
+ * buffer at the given data index.\r
+ * \r
+ * @param check\r
+ * the vector to check against - null will return false.\r
+ * @param buf\r
+ * the buffer to compare data with\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * in the buffer to check against\r
+ * @return true if the data is equivalent, otherwise false.\r
+ */\r
+ public static boolean equals(Vector3f check, FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector3f tempVec3 = vars.vect1;\r
+ populateFromBuffer(tempVec3, buf, index);\r
+ boolean eq = tempVec3.equals(check);\r
+ vars.release();\r
+ return eq;\r
+ }\r
+\r
+ // // -- VECTOR2F METHODS -- ////\r
+ /**\r
+ * Generate a new FloatBuffer using the given array of Vector2f objects.\r
+ * The FloatBuffer will be 2 * data.length long and contain the vector data\r
+ * as data[0].x, data[0].y, data[1].x... etc.\r
+ * \r
+ * @param data array of Vector2f objects to place into a new FloatBuffer\r
+ */\r
+ public static FloatBuffer createFloatBuffer(Vector2f... data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ FloatBuffer buff = createFloatBuffer(2 * data.length);\r
+ for (Vector2f element : data) {\r
+ if (element != null) {\r
+ buff.put(element.x).put(element.y);\r
+ } else {\r
+ buff.put(0).put(0);\r
+ }\r
+ }\r
+ buff.flip();\r
+ return buff;\r
+ }\r
+\r
+ /**\r
+ * Create a new FloatBuffer of an appropriate size to hold the specified\r
+ * number of Vector2f object data.\r
+ * \r
+ * @param vertices\r
+ * number of vertices that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new FloatBuffer\r
+ */\r
+ public static FloatBuffer createVector2Buffer(int vertices) {\r
+ FloatBuffer vBuff = createFloatBuffer(2 * vertices);\r
+ return vBuff;\r
+ }\r
+\r
+ /**\r
+ * Create a new FloatBuffer of an appropriate size to hold the specified\r
+ * number of Vector2f object data only if the given buffer if not already\r
+ * the right size.\r
+ * \r
+ * @param buf\r
+ * the buffer to first check and rewind\r
+ * @param vertices\r
+ * number of vertices that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new FloatBuffer\r
+ */\r
+ public static FloatBuffer createVector2Buffer(FloatBuffer buf, int vertices) {\r
+ if (buf != null && buf.limit() == 2 * vertices) {\r
+ buf.rewind();\r
+ return buf;\r
+ }\r
+\r
+ return createFloatBuffer(2 * vertices);\r
+ }\r
+\r
+ /**\r
+ * Sets the data contained in the given Vector2F into the FloatBuffer at the\r
+ * specified index.\r
+ * \r
+ * @param vector\r
+ * the data to insert\r
+ * @param buf\r
+ * the buffer to insert into\r
+ * @param index\r
+ * the postion to place the data; in terms of vectors not floats\r
+ */\r
+ public static void setInBuffer(Vector2f vector, FloatBuffer buf, int index) {\r
+ buf.put(index * 2, vector.x);\r
+ buf.put((index * 2) + 1, vector.y);\r
+ }\r
+\r
+ /**\r
+ * Updates the values of the given vector from the specified buffer at the\r
+ * index provided.\r
+ * \r
+ * @param vector\r
+ * the vector to set data on\r
+ * @param buf\r
+ * the buffer to read from\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) to read from\r
+ * the buf\r
+ */\r
+ public static void populateFromBuffer(Vector2f vector, FloatBuffer buf, int index) {\r
+ vector.x = buf.get(index * 2);\r
+ vector.y = buf.get(index * 2 + 1);\r
+ }\r
+\r
+ /**\r
+ * Generates a Vector2f array from the given FloatBuffer.\r
+ * \r
+ * @param buff\r
+ * the FloatBuffer to read from\r
+ * @return a newly generated array of Vector2f objects\r
+ */\r
+ public static Vector2f[] getVector2Array(FloatBuffer buff) {\r
+ buff.clear();\r
+ Vector2f[] verts = new Vector2f[buff.limit() / 2];\r
+ for (int x = 0; x < verts.length; x++) {\r
+ Vector2f v = new Vector2f(buff.get(), buff.get());\r
+ verts[x] = v;\r
+ }\r
+ return verts;\r
+ }\r
+\r
+ /**\r
+ * Copies a Vector2f from one position in the buffer to another. The index\r
+ * values are in terms of vector number (eg, vector number 0 is postions 0-1\r
+ * in the FloatBuffer.)\r
+ * \r
+ * @param buf\r
+ * the buffer to copy from/to\r
+ * @param fromPos\r
+ * the index of the vector to copy\r
+ * @param toPos\r
+ * the index to copy the vector to\r
+ */\r
+ public static void copyInternalVector2(FloatBuffer buf, int fromPos, int toPos) {\r
+ copyInternal(buf, fromPos * 2, toPos * 2, 2);\r
+ }\r
+\r
+ /**\r
+ * Normalize a Vector2f in-buffer.\r
+ * \r
+ * @param buf\r
+ * the buffer to find the Vector2f within\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * to normalize\r
+ */\r
+ public static void normalizeVector2(FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector2f tempVec2 = vars.vect2d;\r
+ populateFromBuffer(tempVec2, buf, index);\r
+ tempVec2.normalizeLocal();\r
+ setInBuffer(tempVec2, buf, index);\r
+ vars.release();\r
+ }\r
+\r
+ /**\r
+ * Add to a Vector2f in-buffer.\r
+ * \r
+ * @param toAdd\r
+ * the vector to add from\r
+ * @param buf\r
+ * the buffer to find the Vector2f within\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * to add to\r
+ */\r
+ public static void addInBuffer(Vector2f toAdd, FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector2f tempVec2 = vars.vect2d;\r
+ populateFromBuffer(tempVec2, buf, index);\r
+ tempVec2.addLocal(toAdd);\r
+ setInBuffer(tempVec2, buf, index);\r
+ vars.release();\r
+ }\r
+\r
+ /**\r
+ * Multiply and store a Vector2f in-buffer.\r
+ * \r
+ * @param toMult\r
+ * the vector to multiply against\r
+ * @param buf\r
+ * the buffer to find the Vector2f within\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * to multiply\r
+ */\r
+ public static void multInBuffer(Vector2f toMult, FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector2f tempVec2 = vars.vect2d;\r
+ populateFromBuffer(tempVec2, buf, index);\r
+ tempVec2.multLocal(toMult);\r
+ setInBuffer(tempVec2, buf, index);\r
+ vars.release();\r
+ }\r
+\r
+ /**\r
+ * Checks to see if the given Vector2f is equals to the data stored in the\r
+ * buffer at the given data index.\r
+ * \r
+ * @param check\r
+ * the vector to check against - null will return false.\r
+ * @param buf\r
+ * the buffer to compare data with\r
+ * @param index\r
+ * the position (in terms of vectors, not floats) of the vector\r
+ * in the buffer to check against\r
+ * @return true if the data is equivalent, otherwise false.\r
+ */\r
+ public static boolean equals(Vector2f check, FloatBuffer buf, int index) {\r
+ TempVars vars = TempVars.get();\r
+ Vector2f tempVec2 = vars.vect2d;\r
+ populateFromBuffer(tempVec2, buf, index);\r
+ boolean eq = tempVec2.equals(check);\r
+ vars.release();\r
+ return eq;\r
+ }\r
+\r
+ //// -- INT METHODS -- ////\r
+ /**\r
+ * Generate a new IntBuffer using the given array of ints. The IntBuffer\r
+ * will be data.length long and contain the int data as data[0], data[1]...\r
+ * etc.\r
+ * \r
+ * @param data\r
+ * array of ints to place into a new IntBuffer\r
+ */\r
+ public static IntBuffer createIntBuffer(int... data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ IntBuffer buff = createIntBuffer(data.length);\r
+ buff.clear();\r
+ buff.put(data);\r
+ buff.flip();\r
+ return buff;\r
+ }\r
+\r
+ /**\r
+ * Create a new int[] array and populate it with the given IntBuffer's\r
+ * contents.\r
+ * \r
+ * @param buff\r
+ * the IntBuffer to read from\r
+ * @return a new int array populated from the IntBuffer\r
+ */\r
+ public static int[] getIntArray(IntBuffer buff) {\r
+ if (buff == null) {\r
+ return null;\r
+ }\r
+ buff.clear();\r
+ int[] inds = new int[buff.limit()];\r
+ for (int x = 0; x < inds.length; x++) {\r
+ inds[x] = buff.get();\r
+ }\r
+ return inds;\r
+ }\r
+\r
+ /**\r
+ * Create a new float[] array and populate it with the given FloatBuffer's\r
+ * contents.\r
+ * \r
+ * @param buff\r
+ * the FloatBuffer to read from\r
+ * @return a new float array populated from the FloatBuffer\r
+ */\r
+ public static float[] getFloatArray(FloatBuffer buff) {\r
+ if (buff == null) {\r
+ return null;\r
+ }\r
+ buff.clear();\r
+ float[] inds = new float[buff.limit()];\r
+ for (int x = 0; x < inds.length; x++) {\r
+ inds[x] = buff.get();\r
+ }\r
+ return inds;\r
+ }\r
+\r
+ //// -- GENERAL DOUBLE ROUTINES -- ////\r
+ /**\r
+ * Create a new DoubleBuffer of the specified size.\r
+ * \r
+ * @param size\r
+ * required number of double to store.\r
+ * @return the new DoubleBuffer\r
+ */\r
+ public static DoubleBuffer createDoubleBuffer(int size) {\r
+ DoubleBuffer buf = ByteBuffer.allocateDirect(8 * size).order(ByteOrder.nativeOrder()).asDoubleBuffer();\r
+ buf.clear();\r
+ onBufferAllocated(buf);\r
+ return buf;\r
+ }\r
+\r
+ /**\r
+ * Create a new DoubleBuffer of an appropriate size to hold the specified\r
+ * number of doubles only if the given buffer if not already the right size.\r
+ * \r
+ * @param buf\r
+ * the buffer to first check and rewind\r
+ * @param size\r
+ * number of doubles that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new DoubleBuffer\r
+ */\r
+ public static DoubleBuffer createDoubleBuffer(DoubleBuffer buf, int size) {\r
+ if (buf != null && buf.limit() == size) {\r
+ buf.rewind();\r
+ return buf;\r
+ }\r
+\r
+ buf = createDoubleBuffer(size);\r
+ return buf;\r
+ }\r
+\r
+ /**\r
+ * Creates a new DoubleBuffer with the same contents as the given\r
+ * DoubleBuffer. The new DoubleBuffer is seperate from the old one and\r
+ * changes are not reflected across. If you want to reflect changes,\r
+ * consider using Buffer.duplicate().\r
+ * \r
+ * @param buf\r
+ * the DoubleBuffer to copy\r
+ * @return the copy\r
+ */\r
+ public static DoubleBuffer clone(DoubleBuffer buf) {\r
+ if (buf == null) {\r
+ return null;\r
+ }\r
+ buf.rewind();\r
+\r
+ DoubleBuffer copy;\r
+ if (isDirect(buf)) {\r
+ copy = createDoubleBuffer(buf.limit());\r
+ } else {\r
+ copy = DoubleBuffer.allocate(buf.limit());\r
+ }\r
+ copy.put(buf);\r
+\r
+ return copy;\r
+ }\r
+\r
+ //// -- GENERAL FLOAT ROUTINES -- ////\r
+ /**\r
+ * Create a new FloatBuffer of the specified size.\r
+ * \r
+ * @param size\r
+ * required number of floats to store.\r
+ * @return the new FloatBuffer\r
+ */\r
+ public static FloatBuffer createFloatBuffer(int size) {\r
+ FloatBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asFloatBuffer();\r
+ buf.clear();\r
+ onBufferAllocated(buf);\r
+ return buf;\r
+ }\r
+\r
+ /**\r
+ * Copies floats from one position in the buffer to another.\r
+ * \r
+ * @param buf\r
+ * the buffer to copy from/to\r
+ * @param fromPos\r
+ * the starting point to copy from\r
+ * @param toPos\r
+ * the starting point to copy to\r
+ * @param length\r
+ * the number of floats to copy\r
+ */\r
+ public static void copyInternal(FloatBuffer buf, int fromPos, int toPos, int length) {\r
+ float[] data = new float[length];\r
+ buf.position(fromPos);\r
+ buf.get(data);\r
+ buf.position(toPos);\r
+ buf.put(data);\r
+ }\r
+\r
+ /**\r
+ * Creates a new FloatBuffer with the same contents as the given\r
+ * FloatBuffer. The new FloatBuffer is seperate from the old one and changes\r
+ * are not reflected across. If you want to reflect changes, consider using\r
+ * Buffer.duplicate().\r
+ * \r
+ * @param buf\r
+ * the FloatBuffer to copy\r
+ * @return the copy\r
+ */\r
+ public static FloatBuffer clone(FloatBuffer buf) {\r
+ if (buf == null) {\r
+ return null;\r
+ }\r
+ buf.rewind();\r
+\r
+ FloatBuffer copy;\r
+ if (isDirect(buf)) {\r
+ copy = createFloatBuffer(buf.limit());\r
+ } else {\r
+ copy = FloatBuffer.allocate(buf.limit());\r
+ }\r
+ copy.put(buf);\r
+\r
+ return copy;\r
+ }\r
+\r
+ //// -- GENERAL INT ROUTINES -- ////\r
+ /**\r
+ * Create a new IntBuffer of the specified size.\r
+ * \r
+ * @param size\r
+ * required number of ints to store.\r
+ * @return the new IntBuffer\r
+ */\r
+ public static IntBuffer createIntBuffer(int size) {\r
+ IntBuffer buf = ByteBuffer.allocateDirect(4 * size).order(ByteOrder.nativeOrder()).asIntBuffer();\r
+ buf.clear();\r
+ onBufferAllocated(buf);\r
+ return buf;\r
+ }\r
+\r
+ /**\r
+ * Create a new IntBuffer of an appropriate size to hold the specified\r
+ * number of ints only if the given buffer if not already the right size.\r
+ * \r
+ * @param buf\r
+ * the buffer to first check and rewind\r
+ * @param size\r
+ * number of ints that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new IntBuffer\r
+ */\r
+ public static IntBuffer createIntBuffer(IntBuffer buf, int size) {\r
+ if (buf != null && buf.limit() == size) {\r
+ buf.rewind();\r
+ return buf;\r
+ }\r
+\r
+ buf = createIntBuffer(size);\r
+ return buf;\r
+ }\r
+\r
+ /**\r
+ * Creates a new IntBuffer with the same contents as the given IntBuffer.\r
+ * The new IntBuffer is seperate from the old one and changes are not\r
+ * reflected across. If you want to reflect changes, consider using\r
+ * Buffer.duplicate().\r
+ * \r
+ * @param buf\r
+ * the IntBuffer to copy\r
+ * @return the copy\r
+ */\r
+ public static IntBuffer clone(IntBuffer buf) {\r
+ if (buf == null) {\r
+ return null;\r
+ }\r
+ buf.rewind();\r
+\r
+ IntBuffer copy;\r
+ if (isDirect(buf)) {\r
+ copy = createIntBuffer(buf.limit());\r
+ } else {\r
+ copy = IntBuffer.allocate(buf.limit());\r
+ }\r
+ copy.put(buf);\r
+\r
+ return copy;\r
+ }\r
+\r
+ //// -- GENERAL BYTE ROUTINES -- ////\r
+ /**\r
+ * Create a new ByteBuffer of the specified size.\r
+ * \r
+ * @param size\r
+ * required number of ints to store.\r
+ * @return the new IntBuffer\r
+ */\r
+ public static ByteBuffer createByteBuffer(int size) {\r
+ ByteBuffer buf = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());\r
+ buf.clear();\r
+ onBufferAllocated(buf);\r
+ return buf;\r
+ }\r
+\r
+ /**\r
+ * Create a new ByteBuffer of an appropriate size to hold the specified\r
+ * number of ints only if the given buffer if not already the right size.\r
+ * \r
+ * @param buf\r
+ * the buffer to first check and rewind\r
+ * @param size\r
+ * number of bytes that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new IntBuffer\r
+ */\r
+ public static ByteBuffer createByteBuffer(ByteBuffer buf, int size) {\r
+ if (buf != null && buf.limit() == size) {\r
+ buf.rewind();\r
+ return buf;\r
+ }\r
+\r
+ buf = createByteBuffer(size);\r
+ return buf;\r
+ }\r
+\r
+ public static ByteBuffer createByteBuffer(byte... data) {\r
+ ByteBuffer bb = createByteBuffer(data.length);\r
+ bb.put(data);\r
+ bb.flip();\r
+ return bb;\r
+ }\r
+\r
+ public static ByteBuffer createByteBuffer(String data) {\r
+ byte[] bytes = data.getBytes();\r
+ ByteBuffer bb = createByteBuffer(bytes.length);\r
+ bb.put(bytes);\r
+ bb.flip();\r
+ return bb;\r
+ }\r
+\r
+ /**\r
+ * Creates a new ByteBuffer with the same contents as the given ByteBuffer.\r
+ * The new ByteBuffer is seperate from the old one and changes are not\r
+ * reflected across. If you want to reflect changes, consider using\r
+ * Buffer.duplicate().\r
+ * \r
+ * @param buf\r
+ * the ByteBuffer to copy\r
+ * @return the copy\r
+ */\r
+ public static ByteBuffer clone(ByteBuffer buf) {\r
+ if (buf == null) {\r
+ return null;\r
+ }\r
+ buf.rewind();\r
+\r
+ ByteBuffer copy;\r
+ if (isDirect(buf)) {\r
+ copy = createByteBuffer(buf.limit());\r
+ } else {\r
+ copy = ByteBuffer.allocate(buf.limit());\r
+ }\r
+ copy.put(buf);\r
+\r
+ return copy;\r
+ }\r
+\r
+ //// -- GENERAL SHORT ROUTINES -- ////\r
+ /**\r
+ * Create a new ShortBuffer of the specified size.\r
+ * \r
+ * @param size\r
+ * required number of shorts to store.\r
+ * @return the new ShortBuffer\r
+ */\r
+ public static ShortBuffer createShortBuffer(int size) {\r
+ ShortBuffer buf = ByteBuffer.allocateDirect(2 * size).order(ByteOrder.nativeOrder()).asShortBuffer();\r
+ buf.clear();\r
+ onBufferAllocated(buf);\r
+ return buf;\r
+ }\r
+\r
+ /**\r
+ * Create a new ShortBuffer of an appropriate size to hold the specified\r
+ * number of shorts only if the given buffer if not already the right size.\r
+ * \r
+ * @param buf\r
+ * the buffer to first check and rewind\r
+ * @param size\r
+ * number of shorts that need to be held by the newly created\r
+ * buffer\r
+ * @return the requested new ShortBuffer\r
+ */\r
+ public static ShortBuffer createShortBuffer(ShortBuffer buf, int size) {\r
+ if (buf != null && buf.limit() == size) {\r
+ buf.rewind();\r
+ return buf;\r
+ }\r
+\r
+ buf = createShortBuffer(size);\r
+ return buf;\r
+ }\r
+\r
+ public static ShortBuffer createShortBuffer(short... data) {\r
+ if (data == null) {\r
+ return null;\r
+ }\r
+ ShortBuffer buff = createShortBuffer(data.length);\r
+ buff.clear();\r
+ buff.put(data);\r
+ buff.flip();\r
+ return buff;\r
+ }\r
+\r
+ /**\r
+ * Creates a new ShortBuffer with the same contents as the given ShortBuffer.\r
+ * The new ShortBuffer is seperate from the old one and changes are not\r
+ * reflected across. If you want to reflect changes, consider using\r
+ * Buffer.duplicate().\r
+ * \r
+ * @param buf\r
+ * the ShortBuffer to copy\r
+ * @return the copy\r
+ */\r
+ public static ShortBuffer clone(ShortBuffer buf) {\r
+ if (buf == null) {\r
+ return null;\r
+ }\r
+ buf.rewind();\r
+\r
+ ShortBuffer copy;\r
+ if (isDirect(buf)) {\r
+ copy = createShortBuffer(buf.limit());\r
+ } else {\r
+ copy = ShortBuffer.allocate(buf.limit());\r
+ }\r
+ copy.put(buf);\r
+\r
+ return copy;\r
+ }\r
+\r
+ /**\r
+ * Ensures there is at least the <code>required</code> number of entries left after the current position of the\r
+ * buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer.\r
+ * @param buffer buffer that should be checked/copied (may be null)\r
+ * @param required minimum number of elements that should be remaining in the returned buffer\r
+ * @return a buffer large enough to receive at least the <code>required</code> number of entries, same position as\r
+ * the input buffer, not null\r
+ */\r
+ public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {\r
+ if (buffer != null) {\r
+ buffer.limit(buffer.capacity());\r
+ }\r
+ if (buffer == null || (buffer.remaining() < required)) {\r
+ int position = (buffer != null ? buffer.position() : 0);\r
+ FloatBuffer newVerts = createFloatBuffer(position + required);\r
+ if (buffer != null) {\r
+ buffer.flip();\r
+ newVerts.put(buffer);\r
+ newVerts.position(position);\r
+ }\r
+ buffer = newVerts;\r
+ }\r
+ return buffer;\r
+ }\r
+ \r
+ public static IntBuffer ensureLargeEnough(IntBuffer buffer, int required) {\r
+ if (buffer != null) {\r
+ buffer.limit(buffer.capacity());\r
+ }\r
+ if (buffer == null || (buffer.remaining() < required)) {\r
+ int position = (buffer != null ? buffer.position() : 0);\r
+ IntBuffer newVerts = createIntBuffer(position + required);\r
+ if (buffer != null) {\r
+ buffer.flip();\r
+ newVerts.put(buffer);\r
+ newVerts.position(position);\r
+ }\r
+ buffer = newVerts;\r
+ }\r
+ return buffer;\r
+ }\r
+ \r
+\r
+ public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {\r
+ if (buffer != null) {\r
+ buffer.limit(buffer.capacity());\r
+ }\r
+ if (buffer == null || (buffer.remaining() < required)) {\r
+ int position = (buffer != null ? buffer.position() : 0);\r
+ ShortBuffer newVerts = createShortBuffer(position + required);\r
+ if (buffer != null) {\r
+ buffer.flip();\r
+ newVerts.put(buffer);\r
+ newVerts.position(position);\r
+ }\r
+ buffer = newVerts;\r
+ }\r
+ return buffer;\r
+ }\r
+\r
+ public static ByteBuffer ensureLargeEnough(ByteBuffer buffer, int required) {\r
+ if (buffer != null) {\r
+ buffer.limit(buffer.capacity());\r
+ }\r
+ if (buffer == null || (buffer.remaining() < required)) {\r
+ int position = (buffer != null ? buffer.position() : 0);\r
+ ByteBuffer newVerts = createByteBuffer(position + required);\r
+ if (buffer != null) {\r
+ buffer.flip();\r
+ newVerts.put(buffer);\r
+ newVerts.position(position);\r
+ }\r
+ buffer = newVerts;\r
+ }\r
+ return buffer;\r
+ }\r
+\r
+ public static void printCurrentDirectMemory(StringBuilder store) {\r
+ long totalHeld = 0;\r
+ long heapMem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();\r
+\r
+ boolean printStout = store == null;\r
+ if (store == null) {\r
+ store = new StringBuilder();\r
+ }\r
+ if (trackDirectMemory) {\r
+ // make a new set to hold the keys to prevent concurrency issues.\r
+ int fBufs = 0, bBufs = 0, iBufs = 0, sBufs = 0, dBufs = 0;\r
+ int fBufsM = 0, bBufsM = 0, iBufsM = 0, sBufsM = 0, dBufsM = 0;\r
+ for (BufferInfo b : BufferUtils.trackedBuffers.values()) {\r
+ if (b.type == ByteBuffer.class) {\r
+ totalHeld += b.size;\r
+ bBufsM += b.size;\r
+ bBufs++;\r
+ } else if (b.type == FloatBuffer.class) {\r
+ totalHeld += b.size;\r
+ fBufsM += b.size;\r
+ fBufs++;\r
+ } else if (b.type == IntBuffer.class) {\r
+ totalHeld += b.size;\r
+ iBufsM += b.size;\r
+ iBufs++;\r
+ } else if (b.type == ShortBuffer.class) {\r
+ totalHeld += b.size;\r
+ sBufsM += b.size;\r
+ sBufs++;\r
+ } else if (b.type == DoubleBuffer.class) {\r
+ totalHeld += b.size;\r
+ dBufsM += b.size;\r
+ dBufs++;\r
+ }\r
+ }\r
+\r
+ store.append("Existing buffers: ").append(BufferUtils.trackedBuffers.size()).append("\n");\r
+ store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs).append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n");\r
+ store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");\r
+ store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");\r
+ 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");\r
+ } else {\r
+ store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");\r
+ store.append("Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.").append("\n");\r
+ }\r
+ if (printStout) {\r
+ System.out.println(store.toString());\r
+ }\r
+ }\r
+ private static final AtomicBoolean loadedMethods = new AtomicBoolean(false);\r
+ private static Method cleanerMethod = null;\r
+ private static Method cleanMethod = null;\r
+ private static Method viewedBufferMethod = null;\r
+ private static Method freeMethod = null;\r
+\r
+ private static Method loadMethod(String className, String methodName) {\r
+ try {\r
+ Method method = Class.forName(className).getMethod(methodName);\r
+ method.setAccessible(true);\r
+ return method;\r
+ } catch (NoSuchMethodException ex) {\r
+ return null; // the method was not found\r
+ } catch (SecurityException ex) {\r
+ return null; // setAccessible not allowed by security policy\r
+ } catch (ClassNotFoundException ex) {\r
+ return null; // the direct buffer implementation was not found\r
+ }\r
+ }\r
+\r
+ private static void loadCleanerMethods() {\r
+ // If its already true, exit, if not, set it to true.\r
+ if (BufferUtils.loadedMethods.getAndSet(true)) {\r
+ return;\r
+ }\r
+ // This could potentially be called many times if used from multiple\r
+ // threads\r
+ synchronized (BufferUtils.loadedMethods) {\r
+ // Oracle JRE / OpenJDK\r
+ cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner");\r
+ cleanMethod = loadMethod("sun.misc.Cleaner", "clean");\r
+ viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer");\r
+ if (viewedBufferMethod == null) {\r
+ // They changed the name in Java 7 (???)\r
+ viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "attachment");\r
+ }\r
+\r
+ // Apache Harmony\r
+ ByteBuffer bb = BufferUtils.createByteBuffer(1);\r
+ Class<?> clazz = bb.getClass();\r
+ try {\r
+ freeMethod = clazz.getMethod("free");\r
+ } catch (NoSuchMethodException ex) {\r
+ } catch (SecurityException ex) {\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Direct buffers are garbage collected by using a phantom reference and a\r
+ * reference queue. Every once a while, the JVM checks the reference queue and\r
+ * cleans the direct buffers. However, as this doesn't happen\r
+ * immediately after discarding all references to a direct buffer, it's\r
+ * easy to OutOfMemoryError yourself using direct buffers. This function\r
+ * explicitly calls the Cleaner method of a direct buffer.\r
+ * \r
+ * @param toBeDestroyed\r
+ * The direct buffer that will be "cleaned". Utilizes reflection.\r
+ * \r
+ */\r
+ public static void destroyDirectBuffer(Buffer toBeDestroyed) {\r
+ if (!isDirect(toBeDestroyed)) {\r
+ return;\r
+ }\r
+\r
+ BufferUtils.loadCleanerMethods();\r
+\r
+ try {\r
+ if (freeMethod != null) {\r
+ freeMethod.invoke(toBeDestroyed);\r
+ } else {\r
+ Object cleaner = cleanerMethod.invoke(toBeDestroyed);\r
+ if (cleaner != null) {\r
+ cleanMethod.invoke(cleaner);\r
+ } else {\r
+ // Try the alternate approach of getting the viewed buffer first\r
+ Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed);\r
+ if (viewedBuffer != null) {\r
+ destroyDirectBuffer((Buffer) viewedBuffer);\r
+ } else {\r
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed);\r
+ }\r
+ }\r
+ }\r
+ } catch (IllegalAccessException ex) {\r
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);\r
+ } catch (IllegalArgumentException ex) {\r
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);\r
+ } catch (InvocationTargetException ex) {\r
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);\r
+ } catch (SecurityException ex) {\r
+ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex);\r
+ }\r
+ }\r
+ \r
+ /*\r
+ * FIXME when java 1.5 supprt is dropped - replace calls to this method with Buffer.isDirect \r
+ * \r
+ * Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer subclasses : \r
+ * FloatBuffer, IntBuffer, ShortBuffer, ByteBuffer,DoubleBuffer, LongBuffer. \r
+ * CharBuffer has been excluded as we don't use it.\r
+ * \r
+ */\r
+ private static boolean isDirect(Buffer buf) {\r
+ if (buf instanceof FloatBuffer) {\r
+ return ((FloatBuffer) buf).isDirect();\r
+ }\r
+ if (buf instanceof IntBuffer) {\r
+ return ((IntBuffer) buf).isDirect();\r
+ }\r
+ if (buf instanceof ShortBuffer) {\r
+ return ((ShortBuffer) buf).isDirect();\r
+ }\r
+ if (buf instanceof ByteBuffer) {\r
+ return ((ByteBuffer) buf).isDirect();\r
+ }\r
+ if (buf instanceof DoubleBuffer) {\r
+ return ((DoubleBuffer) buf).isDirect();\r
+ }\r
+ if (buf instanceof LongBuffer) {\r
+ return ((LongBuffer) buf).isDirect();\r
+ }\r
+ throw new UnsupportedOperationException(" BufferUtils.isDirect was called on " + buf.getClass().getName());\r
+ }\r
+\r
+ private static class BufferInfo extends PhantomReference<Buffer> {\r
+\r
+ private Class type;\r
+ private int size;\r
+\r
+ public BufferInfo(Class type, int size, Buffer referent, ReferenceQueue<? super Buffer> q) {\r
+ super(referent, q);\r
+ this.type = type;\r
+ this.size = size;\r
+ }\r
+ }\r
+\r
+ private static class ClearReferences extends Thread {\r
+\r
+ ClearReferences() {\r
+ this.setDaemon(true);\r
+ }\r
+\r
+ @Override\r
+ public void run() {\r
+ try {\r
+ while (true) {\r
+ Reference<? extends Buffer> toclean = BufferUtils.removeCollected.remove();\r
+ BufferUtils.trackedBuffers.remove(toclean);\r
+ }\r
+\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ }\r
+}\r