diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
index 2a7b54023..62741253a 100644
--- a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
+++ b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java
@@ -43,6 +43,9 @@ import java.nio.ShortBuffer;
public class AndroidGL implements GL, GLExt {
+ public void resetStats() {
+ }
+
private static int getLimitBytes(ByteBuffer buffer) {
checkLimit(buffer);
return buffer.limit();
diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
index 9472bff0b..991ad25c0 100644
--- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
+++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java
@@ -54,6 +54,7 @@ import com.jme3.input.dummy.DummyMouseInput;
import com.jme3.renderer.android.AndroidGL;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GLExt;
+import com.jme3.renderer.opengl.GLFbo;
import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.system.*;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -196,7 +197,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex
Object gl = new AndroidGL();
// gl = GLTracer.createGlesTracer((GL)gl, (GLExt)gl);
// gl = new GLDebugES((GL)gl, (GLExt)gl);
- renderer = new GLRenderer((GL)gl, (GLExt)gl);
+ renderer = new GLRenderer((GL)gl, (GLExt)gl, (GLFbo)gl);
renderer.initialize();
JmeSystem.setSoftTextDialogInput(this);
diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java
index c4c0f3995..baad952a0 100644
--- a/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java
+++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java
@@ -195,7 +195,7 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl
/**
* When set to true, the physics coordinates will be applied to the local
- * translation of the Spatial instead of the world traslation.
+ * translation of the Spatial instead of the world translation.
* @param applyPhysicsLocal
*/
public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
diff --git a/jme3-core/src/main/java/com/jme3/animation/Bone.java b/jme3-core/src/main/java/com/jme3/animation/Bone.java
index 819a77383..a78b4b931 100644
--- a/jme3-core/src/main/java/com/jme3/animation/Bone.java
+++ b/jme3-core/src/main/java/com/jme3/animation/Bone.java
@@ -313,6 +313,26 @@ public final class Bone implements Savable {
return modelBindInverseScale;
}
+ public Transform getModelBindInverseTransform() {
+ Transform t = new Transform();
+ t.setTranslation(modelBindInversePos);
+ t.setRotation(modelBindInverseRot);
+ if (modelBindInverseScale != null) {
+ t.setScale(modelBindInverseScale);
+ }
+ return t;
+ }
+
+ public Transform getBindInverseTransform() {
+ Transform t = new Transform();
+ t.setTranslation(bindPos);
+ t.setRotation(bindRot);
+ if (bindScale != null) {
+ t.setScale(bindScale);
+ }
+ return t.invert();
+ }
+
/**
* @deprecated use {@link #getBindPosition()}
*/
diff --git a/jme3-core/src/main/java/com/jme3/math/FastMath.java b/jme3-core/src/main/java/com/jme3/math/FastMath.java
index 5e230dabb..d9d944057 100644
--- a/jme3-core/src/main/java/com/jme3/math/FastMath.java
+++ b/jme3-core/src/main/java/com/jme3/math/FastMath.java
@@ -902,6 +902,25 @@ final public class FastMath {
return clamp(input, 0f, 1f);
}
+ /**
+ * Determine if two floats are approximately equal.
+ * This takes into account the magnitude of the floats, since
+ * large numbers will have larger differences be close to each other.
+ *
+ * Should return true for a=100000, b=100001, but false for a=10000, b=10001.
+ *
+ * @param a The first float to compare
+ * @param b The second float to compare
+ * @return True if a and b are approximately equal, false otherwise.
+ */
+ public static boolean approximateEquals(float a, float b) {
+ if (a == b) {
+ return true;
+ } else {
+ return (abs(a - b) / Math.max(abs(a), abs(b))) <= 0.00001f;
+ }
+ }
+
/**
* Converts a single precision (32 bit) floating point value
* into half precision (16 bit).
diff --git a/jme3-core/src/main/java/com/jme3/math/Transform.java b/jme3-core/src/main/java/com/jme3/math/Transform.java
index 79992ed2b..ac7a324d0 100644
--- a/jme3-core/src/main/java/com/jme3/math/Transform.java
+++ b/jme3-core/src/main/java/com/jme3/math/Transform.java
@@ -259,6 +259,26 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable
return store;
}
+ public Matrix4f toTransformMatrix() {
+ Matrix4f trans = new Matrix4f();
+ trans.setTranslation(translation);
+ trans.setRotationQuaternion(rot);
+ trans.setScale(scale);
+ return trans;
+ }
+
+ public void fromTransformMatrix(Matrix4f mat) {
+ translation.set(mat.toTranslationVector());
+ rot.set(mat.toRotationQuat());
+ scale.set(mat.toScaleVector());
+ }
+
+ public Transform invert() {
+ Transform t = new Transform();
+ t.fromTransformMatrix(toTransformMatrix().invertLocal());
+ return t;
+ }
+
/**
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
*/
diff --git a/jme3-core/src/main/java/com/jme3/renderer/Caps.java b/jme3-core/src/main/java/com/jme3/renderer/Caps.java
index f0d42c8f2..9387b687e 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/Caps.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/Caps.java
@@ -337,7 +337,19 @@ public enum Caps {
*
* Improves the quality of environment mapping.
*/
- SeamlessCubemap;
+ SeamlessCubemap,
+
+ /**
+ * Running with OpenGL 3.2+ core profile.
+ *
+ * Compatibility features will not be available.
+ */
+ CoreProfile,
+
+ /**
+ * GPU can provide and accept binary shaders.
+ */
+ BinaryShader;
/**
* Returns true if given the renderer capabilities, the texture
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
index 9c73838e2..76eedb521 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java
@@ -32,7 +32,6 @@
package com.jme3.renderer.opengl;
import java.nio.ByteBuffer;
-import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
@@ -178,6 +177,8 @@ public interface GL {
public static final int GL_VERTEX_SHADER = 0x8B31;
public static final int GL_ZERO = 0x0;
+ public void resetStats();
+
public void glActiveTexture(int texture);
public void glAttachShader(int program, int shader);
public void glBindBuffer(int target, int buffer);
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java
index 50eb065ab..190ed4547 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java
@@ -35,14 +35,18 @@ import java.nio.IntBuffer;
/**
* GL functions only available on vanilla desktop OpenGL 3.0.
- *
+ *
* @author Kirill Vainer
*/
public interface GL3 extends GL2 {
-
+
public static final int GL_DEPTH_STENCIL_ATTACHMENT = 0x821A;
- public static final int GL_GEOMETRY_SHADER=0x8DD9;
+ public static final int GL_GEOMETRY_SHADER = 0x8DD9;
+ public static final int GL_NUM_EXTENSIONS = 0x821D;
+
public void glBindFragDataLocation(int param1, int param2, String param3); /// GL3+
public void glBindVertexArray(int param1); /// GL3+
+ public void glDeleteVertexArrays(IntBuffer arrays); /// GL3+
public void glGenVertexArrays(IntBuffer param1); /// GL3+
+ public String glGetString(int param1, int param2); /// GL3+
}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java
index e13402bd5..a0511f6ad 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java
@@ -3,15 +3,17 @@ package com.jme3.renderer.opengl;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
-public class GLDebugDesktop extends GLDebugES implements GL2, GL3 {
+public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
private final GL2 gl2;
private final GL3 gl3;
+ private final GL4 gl4;
- public GLDebugDesktop(GL gl, GLFbo glfbo) {
- super(gl, glfbo);
+ public GLDebugDesktop(GL gl, GLExt glext, GLFbo glfbo) {
+ super(gl, glext, glfbo);
this.gl2 = gl instanceof GL2 ? (GL2) gl : null;
this.gl3 = gl instanceof GL3 ? (GL3) gl : null;
+ this.gl4 = gl instanceof GL4 ? (GL4) gl : null;
}
public void glAlphaFunc(int func, float ref) {
@@ -73,5 +75,23 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3 {
gl3.glGenVertexArrays(param1);
checkError();
}
+
+ @Override
+ public String glGetString(int param1, int param2) {
+ String result = gl3.glGetString(param1, param2);
+ checkError();
+ return result;
+ }
+
+ @Override
+ public void glDeleteVertexArrays(IntBuffer arrays) {
+ gl3.glDeleteVertexArrays(arrays);
+ checkError();
+ }
+ @Override
+ public void glPatchParameter(int count) {
+ gl4.glPatchParameter(count);
+ checkError();
+ }
}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java
index 259c56406..2348bd3cd 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java
@@ -10,14 +10,16 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
private final GLFbo glfbo;
private final GLExt glext;
- public GLDebugES(GL gl, GLFbo glfbo) {
+ public GLDebugES(GL gl, GLExt glext, GLFbo glfbo) {
this.gl = gl;
-// this.gl2 = gl instanceof GL2 ? (GL2) gl : null;
-// this.gl3 = gl instanceof GL3 ? (GL3) gl : null;
+ this.glext = glext;
this.glfbo = glfbo;
- this.glext = glfbo instanceof GLExt ? (GLExt) glfbo : null;
}
+ public void resetStats() {
+ gl.resetStats();
+ }
+
public void glActiveTexture(int texture) {
gl.glActiveTexture(texture);
checkError();
@@ -478,7 +480,7 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
}
public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
- glext.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
checkError();
}
@@ -525,7 +527,7 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
}
public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
- glext.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+ glfbo.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
checkError();
}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java
index b0e0b5f78..27f0eb8bd 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java
@@ -41,7 +41,7 @@ import java.nio.IntBuffer;
*
* @author Kirill Vainer
*/
-public interface GLExt extends GLFbo {
+public interface GLExt {
public static final int GL_ALREADY_SIGNALED = 0x911A;
public static final int GL_COMPRESSED_RGB8_ETC2 = 0x9274;
@@ -100,7 +100,6 @@ public interface GLExt extends GLFbo {
public static final int GL_UNSIGNED_INT_5_9_9_9_REV_EXT = 0x8C3E;
public static final int GL_WAIT_FAILED = 0x911D;
- public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
public void glBufferData(int target, IntBuffer data, int usage);
public void glBufferSubData(int target, long offset, IntBuffer data);
public int glClientWaitSync(Object sync, int flags, long timeout);
@@ -110,7 +109,6 @@ public interface GLExt extends GLFbo {
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount);
public Object glFenceSync(int condition, int flags);
public void glGetMultisample(int pname, int index, FloatBuffer val);
- public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height);
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations);
public void glVertexAttribDivisorARB(int index, int divisor);
}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLFbo.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLFbo.java
index 252619db8..737019ce2 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLFbo.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLFbo.java
@@ -83,6 +83,7 @@ public interface GLFbo {
public void glBindFramebufferEXT(int param1, int param2);
public void glBindRenderbufferEXT(int param1, int param2);
+ public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
public int glCheckFramebufferStatusEXT(int param1);
public void glDeleteFramebuffersEXT(IntBuffer param1);
public void glDeleteRenderbuffersEXT(IntBuffer param1);
@@ -92,5 +93,5 @@ public interface GLFbo {
public void glGenRenderbuffersEXT(IntBuffer param1);
public void glGenerateMipmapEXT(int param1);
public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4);
-
+ public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height);
}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java
index 423ae909e..a7ef9f52d 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java
@@ -89,9 +89,11 @@ public final class GLImageFormats {
GLImageFormat[][] formatToGL = new GLImageFormat[2][Image.Format.values().length];
if (caps.contains(Caps.OpenGL20)) {
- format(formatToGL, Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
- format(formatToGL, Format.Luminance8, GL2.GL_LUMINANCE8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
- format(formatToGL, Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ if (!caps.contains(Caps.CoreProfile)) {
+ format(formatToGL, Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
+ format(formatToGL, Format.Luminance8, GL2.GL_LUMINANCE8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
+ format(formatToGL, Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ }
format(formatToGL, Format.RGB8, GL2.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
format(formatToGL, Format.RGBA8, GLExt.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
format(formatToGL, Format.RGB565, GL2.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
@@ -108,8 +110,10 @@ public final class GLImageFormats {
formatSrgb(formatToGL, Format.RGB565, GLExt.GL_SRGB8_EXT, GL.GL_RGB, GL.GL_UNSIGNED_SHORT_5_6_5);
formatSrgb(formatToGL, Format.RGB5A1, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
formatSrgb(formatToGL, Format.RGBA8, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
- formatSrgb(formatToGL, Format.Luminance8, GLExt.GL_SLUMINANCE8_EXT, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
- formatSrgb(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SLUMINANCE8_ALPHA8_EXT, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ if (!caps.contains(Caps.CoreProfile)) {
+ formatSrgb(formatToGL, Format.Luminance8, GLExt.GL_SLUMINANCE8_EXT, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
+ formatSrgb(formatToGL, Format.Luminance8Alpha8, GLExt.GL_SLUMINANCE8_ALPHA8_EXT, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ }
formatSrgb(formatToGL, Format.BGR8, GLExt.GL_SRGB8_EXT, GL2.GL_BGR, GL.GL_UNSIGNED_BYTE);
formatSrgb(formatToGL, Format.ABGR8, GLExt.GL_SRGB8_ALPHA8_EXT, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8);
formatSrgb(formatToGL, Format.ARGB8, GLExt.GL_SRGB8_ALPHA8_EXT, GL2.GL_BGRA, GL2.GL_UNSIGNED_INT_8_8_8_8);
@@ -124,16 +128,20 @@ public final class GLImageFormats {
}
} else if (caps.contains(Caps.Rgba8)) {
// A more limited form of 32-bit RGBA. Only GL_RGBA8 is available.
- format(formatToGL, Format.Alpha8, GLExt.GL_RGBA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
- format(formatToGL, Format.Luminance8, GLExt.GL_RGBA8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
- format(formatToGL, Format.Luminance8Alpha8, GLExt.GL_RGBA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ if (!caps.contains(Caps.CoreProfile)) {
+ format(formatToGL, Format.Alpha8, GLExt.GL_RGBA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
+ format(formatToGL, Format.Luminance8, GLExt.GL_RGBA8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
+ format(formatToGL, Format.Luminance8Alpha8, GLExt.GL_RGBA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ }
format(formatToGL, Format.RGB8, GLExt.GL_RGBA8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
format(formatToGL, Format.RGBA8, GLExt.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
} else {
// Actually, the internal format isn't used for OpenGL ES 2! This is the same as the above..
- format(formatToGL, Format.Alpha8, GL.GL_RGBA4, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
- format(formatToGL, Format.Luminance8, GL.GL_RGB565, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
- format(formatToGL, Format.Luminance8Alpha8, GL.GL_RGBA4, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ if (!caps.contains(Caps.CoreProfile)) {
+ format(formatToGL, Format.Alpha8, GL.GL_RGBA4, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE);
+ format(formatToGL, Format.Luminance8, GL.GL_RGB565, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE);
+ format(formatToGL, Format.Luminance8Alpha8, GL.GL_RGBA4, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE);
+ }
format(formatToGL, Format.RGB8, GL.GL_RGB565, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
format(formatToGL, Format.RGBA8, GL.GL_RGBA4, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
}
@@ -145,9 +153,11 @@ public final class GLImageFormats {
format(formatToGL, Format.RGB5A1, GL.GL_RGB5_A1, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1);
if (caps.contains(Caps.FloatTexture)) {
- format(formatToGL, Format.Luminance16F, GLExt.GL_LUMINANCE16F_ARB, GL.GL_LUMINANCE, GLExt.GL_HALF_FLOAT_ARB);
- format(formatToGL, Format.Luminance32F, GLExt.GL_LUMINANCE32F_ARB, GL.GL_LUMINANCE, GL.GL_FLOAT);
- format(formatToGL, Format.Luminance16FAlpha16F, GLExt.GL_LUMINANCE_ALPHA16F_ARB, GL.GL_LUMINANCE_ALPHA, GLExt.GL_HALF_FLOAT_ARB);
+ if (!caps.contains(Caps.CoreProfile)) {
+ format(formatToGL, Format.Luminance16F, GLExt.GL_LUMINANCE16F_ARB, GL.GL_LUMINANCE, GLExt.GL_HALF_FLOAT_ARB);
+ format(formatToGL, Format.Luminance32F, GLExt.GL_LUMINANCE32F_ARB, GL.GL_LUMINANCE, GL.GL_FLOAT);
+ format(formatToGL, Format.Luminance16FAlpha16F, GLExt.GL_LUMINANCE_ALPHA16F_ARB, GL.GL_LUMINANCE_ALPHA, GLExt.GL_HALF_FLOAT_ARB);
+ }
format(formatToGL, Format.RGB16F, GLExt.GL_RGB16F_ARB, GL.GL_RGB, GLExt.GL_HALF_FLOAT_ARB);
format(formatToGL, Format.RGB32F, GLExt.GL_RGB32F_ARB, GL.GL_RGB, GL.GL_FLOAT);
format(formatToGL, Format.RGBA16F, GLExt.GL_RGBA16F_ARB, GL.GL_RGBA, GLExt.GL_HALF_FLOAT_ARB);
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
index 9ae1c8cc7..db9b743f0 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
@@ -56,6 +56,7 @@ import com.jme3.util.BufferUtils;
import com.jme3.util.ListMap;
import com.jme3.util.NativeObjectManager;
import java.nio.*;
+import java.util.Arrays;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashSet;
@@ -112,13 +113,13 @@ public class GLRenderer implements Renderer {
private final GLFbo glfbo;
private final TextureUtil texUtil;
- public GLRenderer(GL gl, GLFbo glfbo) {
+ public GLRenderer(GL gl, GLExt glext, GLFbo glfbo) {
this.gl = gl;
this.gl2 = gl instanceof GL2 ? (GL2)gl : null;
this.gl3 = gl instanceof GL3 ? (GL3)gl : null;
this.gl4 = gl instanceof GL4 ? (GL4)gl : null;
this.glfbo = glfbo;
- this.glext = glfbo instanceof GLExt ? (GLExt)glfbo : null;
+ this.glext = glext;
this.texUtil = new TextureUtil(gl, gl2, glext, context);
}
@@ -137,10 +138,19 @@ public class GLRenderer implements Renderer {
return limits;
}
- private static HashSet loadExtensions(String extensions) {
+ private HashSet loadExtensions() {
HashSet extensionSet = new HashSet(64);
- for (String extension : extensions.split(" ")) {
- extensionSet.add(extension);
+ if (gl3 != null) {
+ // If OpenGL3+ is available, use the non-deprecated way
+ // of getting supported extensions.
+ gl3.glGetInteger(GL3.GL_NUM_EXTENSIONS, intBuf16);
+ int extensionCount = intBuf16.get(0);
+ for (int i = 0; i < extensionCount; i++) {
+ String extension = gl3.glGetString(GL.GL_EXTENSIONS, i);
+ extensionSet.add(extension);
+ }
+ } else {
+ extensionSet.addAll(Arrays.asList(gl.glGetString(GL.GL_EXTENSIONS).split(" ")));
}
return extensionSet;
}
@@ -185,10 +195,12 @@ public class GLRenderer implements Renderer {
caps.add(Caps.OpenGL31);
if (oglVer >= 320) {
caps.add(Caps.OpenGL32);
- }if(oglVer>=330){
+ }
+ if (oglVer >= 330) {
caps.add(Caps.OpenGL33);
caps.add(Caps.GeometryShader);
- }if(oglVer>=400){
+ }
+ if (oglVer >= 400) {
caps.add(Caps.OpenGL40);
caps.add(Caps.TesselationShader);
}
@@ -243,7 +255,7 @@ public class GLRenderer implements Renderer {
}
private void loadCapabilitiesCommon() {
- extensions = loadExtensions(gl.glGetString(GL.GL_EXTENSIONS));
+ extensions = loadExtensions();
limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS));
if (limits.get(Limits.VertexTextureUnits) > 0) {
@@ -251,7 +263,7 @@ public class GLRenderer implements Renderer {
}
limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS));
-
+
// gl.glGetInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
// vertexUniforms = intBuf16.get(0);
// logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
@@ -279,7 +291,7 @@ public class GLRenderer implements Renderer {
// == texture format extensions ==
- boolean hasFloatTexture = false;
+ boolean hasFloatTexture;
hasFloatTexture = hasExtension("GL_OES_texture_half_float") &&
hasExtension("GL_OES_texture_float");
@@ -375,11 +387,11 @@ public class GLRenderer implements Renderer {
caps.add(Caps.TextureFilterAnisotropic);
}
- if (hasExtension("GL_EXT_framebuffer_object")) {
+ if (hasExtension("GL_EXT_framebuffer_object") || gl3 != null) {
caps.add(Caps.FrameBuffer);
- limits.put(Limits.RenderBufferSize, getInteger(GLExt.GL_MAX_RENDERBUFFER_SIZE_EXT));
- limits.put(Limits.FrameBufferAttachments, getInteger(GLExt.GL_MAX_COLOR_ATTACHMENTS_EXT));
+ limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT));
+ limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT));
if (hasExtension("GL_EXT_framebuffer_blit")) {
caps.add(Caps.FrameBufferBlit);
@@ -434,21 +446,30 @@ public class GLRenderer implements Renderer {
caps.add(Caps.SeamlessCubemap);
}
-// if (hasExtension("GL_ARB_get_program_binary")) {
-// int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS);
-// }
+ if (caps.contains(Caps.OpenGL32) && !hasExtension("GL_ARB_compatibility")) {
+ caps.add(Caps.CoreProfile);
+ }
+
+ if (hasExtension("GL_ARB_get_program_binary")) {
+ int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS);
+ if (binaryFormats > 0) {
+ caps.add(Caps.BinaryShader);
+ }
+ }
// Print context information
logger.log(Level.INFO, "OpenGL Renderer Information\n" +
" * Vendor: {0}\n" +
" * Renderer: {1}\n" +
" * OpenGL Version: {2}\n" +
- " * GLSL Version: {3}",
+ " * GLSL Version: {3}\n" +
+ " * Profile: {4}",
new Object[]{
gl.glGetString(GL.GL_VENDOR),
gl.glGetString(GL.GL_RENDERER),
gl.glGetString(GL.GL_VERSION),
- gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)
+ gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION),
+ caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility"
});
// Print capabilities (if fine logging is enabled)
@@ -491,6 +512,20 @@ public class GLRenderer implements Renderer {
// Initialize default state..
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
+
+ if (caps.contains(Caps.CoreProfile)) {
+ // Core Profile requires VAO to be bound.
+ gl3.glGenVertexArrays(intBuf16);
+ int vaoId = intBuf16.get(0);
+ gl3.glBindVertexArray(vaoId);
+ }
+ if (gl2 != null) {
+ gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
+ if (!caps.contains(Caps.CoreProfile)) {
+ gl2.glEnable(GL2.GL_POINT_SPRITE);
+ context.pointSprite = true;
+ }
+ }
}
public void invalidateState() {
@@ -610,31 +645,6 @@ public class GLRenderer implements Renderer {
context.colorWriteEnabled = false;
}
- if (gl2 != null) {
- if (state.isPointSprite() && !context.pointSprite) {
- // Only enable/disable sprite
- if (context.boundTextures[0] != null) {
- if (context.boundTextureUnit != 0) {
- gl.glActiveTexture(GL.GL_TEXTURE0);
- context.boundTextureUnit = 0;
- }
- gl2.glEnable(GL2.GL_POINT_SPRITE);
- gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
- }
- context.pointSprite = true;
- } else if (!state.isPointSprite() && context.pointSprite) {
- if (context.boundTextures[0] != null) {
- if (context.boundTextureUnit != 0) {
- gl.glActiveTexture(GL.GL_TEXTURE0);
- context.boundTextureUnit = 0;
- }
- gl2.glDisable(GL2.GL_POINT_SPRITE);
- gl2.glDisable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
- context.pointSprite = false;
- }
- }
- }
-
if (state.isPolyOffset()) {
if (!context.polyOffsetEnabled) {
gl.glEnable(GL.GL_POLYGON_OFFSET_FILL);
@@ -704,9 +714,6 @@ public class GLRenderer implements Renderer {
case AlphaAdditive:
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
break;
- case Color:
- gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR);
- break;
case Alpha:
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
break;
@@ -719,6 +726,7 @@ public class GLRenderer implements Renderer {
case ModulateX2:
gl.glBlendFunc(GL.GL_DST_COLOR, GL.GL_SRC_COLOR);
break;
+ case Color:
case Screen:
gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR);
break;
@@ -862,6 +870,7 @@ public class GLRenderer implements Renderer {
public void postFrame() {
objManager.deleteUnused(this);
+ gl.resetStats();
}
/*********************************************************************\
@@ -1290,24 +1299,24 @@ public class GLRenderer implements Renderer {
}
if (src == null) {
- glfbo.glBindFramebufferEXT(GLExt.GL_READ_FRAMEBUFFER_EXT, 0);
+ glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, 0);
srcX0 = vpX;
srcY0 = vpY;
srcX1 = vpX + vpW;
srcY1 = vpY + vpH;
} else {
- glfbo.glBindFramebufferEXT(GLExt.GL_READ_FRAMEBUFFER_EXT, src.getId());
+ glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, src.getId());
srcX1 = src.getWidth();
srcY1 = src.getHeight();
}
if (dst == null) {
- glfbo.glBindFramebufferEXT(GLExt.GL_DRAW_FRAMEBUFFER_EXT, 0);
+ glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, 0);
dstX0 = vpX;
dstY0 = vpY;
dstX1 = vpX + vpW;
dstY1 = vpY + vpH;
} else {
- glfbo.glBindFramebufferEXT(GLExt.GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
+ glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, dst.getId());
dstX1 = dst.getWidth();
dstY1 = dst.getHeight();
}
@@ -1315,12 +1324,12 @@ public class GLRenderer implements Renderer {
if (copyDepth) {
mask |= GL.GL_DEPTH_BUFFER_BIT;
}
- glext.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
+ glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
dstX0, dstY0, dstX1, dstY1, mask,
GL.GL_NEAREST);
- glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, prevFBO);
+ glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, prevFBO);
} else {
throw new RendererException("Framebuffer blitting not supported by the video hardware");
}
@@ -1366,7 +1375,7 @@ public class GLRenderer implements Renderer {
}
if (context.boundRB != id) {
- glfbo.glBindRenderbufferEXT(GLExt.GL_RENDERBUFFER_EXT, id);
+ glfbo.glBindRenderbufferEXT(GLFbo.GL_RENDERBUFFER_EXT, id);
context.boundRB = id;
}
@@ -1384,13 +1393,13 @@ public class GLRenderer implements Renderer {
if (maxSamples < samples) {
samples = maxSamples;
}
- glext.glRenderbufferStorageMultisampleEXT(GLExt.GL_RENDERBUFFER_EXT,
+ glfbo.glRenderbufferStorageMultisampleEXT(GLFbo.GL_RENDERBUFFER_EXT,
samples,
glFmt.internalFormat,
fb.getWidth(),
fb.getHeight());
} else {
- glfbo.glRenderbufferStorageEXT(GLExt.GL_RENDERBUFFER_EXT,
+ glfbo.glRenderbufferStorageEXT(GLFbo.GL_RENDERBUFFER_EXT,
glFmt.internalFormat,
fb.getWidth(),
fb.getHeight());
@@ -1400,7 +1409,7 @@ public class GLRenderer implements Renderer {
private int convertAttachmentSlot(int attachmentSlot) {
// can also add support for stencil here
if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
- return GLExt.GL_DEPTH_ATTACHMENT_EXT;
+ return GLFbo.GL_DEPTH_ATTACHMENT_EXT;
} else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
// NOTE: Using depth stencil format requires GL3, this is already
// checked via render caps.
@@ -1409,7 +1418,7 @@ public class GLRenderer implements Renderer {
throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
}
- return GLExt.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
+ return GLFbo.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot;
}
public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
@@ -1427,7 +1436,7 @@ public class GLRenderer implements Renderer {
setupTextureParams(tex);
}
- glfbo.glFramebufferTexture2DEXT(GLExt.GL_FRAMEBUFFER_EXT,
+ glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT,
convertAttachmentSlot(rb.getSlot()),
convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()),
image.getId(),
@@ -1445,9 +1454,9 @@ public class GLRenderer implements Renderer {
updateRenderTexture(fb, rb);
}
if (needAttach) {
- glfbo.glFramebufferRenderbufferEXT(GLExt.GL_FRAMEBUFFER_EXT,
+ glfbo.glFramebufferRenderbufferEXT(GLFbo.GL_FRAMEBUFFER_EXT,
convertAttachmentSlot(rb.getSlot()),
- GLExt.GL_RENDERBUFFER_EXT,
+ GLFbo.GL_RENDERBUFFER_EXT,
rb.getId());
}
}
@@ -1465,7 +1474,7 @@ public class GLRenderer implements Renderer {
}
if (context.boundFBO != id) {
- glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, id);
+ glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, id);
// binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
context.boundDrawBuf = 0;
context.boundFBO = id;
@@ -1545,7 +1554,7 @@ public class GLRenderer implements Renderer {
if (fb == null) {
// unbind any fbos
if (context.boundFBO != 0) {
- glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, 0);
+ glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
statistics.onFrameBufferUse(null, true);
context.boundFBO = 0;
@@ -1577,7 +1586,7 @@ public class GLRenderer implements Renderer {
setViewPort(0, 0, fb.getWidth(), fb.getHeight());
if (context.boundFBO != fb.getId()) {
- glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, fb.getId());
+ glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
statistics.onFrameBufferUse(fb, true);
context.boundFBO = fb.getId();
@@ -1617,7 +1626,7 @@ public class GLRenderer implements Renderer {
if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
intBuf16.clear();
for (int i = 0; i < fb.getNumColorBuffers(); i++) {
- intBuf16.put(GLExt.GL_COLOR_ATTACHMENT0_EXT + i);
+ intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i);
}
intBuf16.flip();
@@ -1629,7 +1638,7 @@ public class GLRenderer implements Renderer {
// select this draw buffer
if (gl2 != null) {
if (context.boundDrawBuf != rb.getSlot()) {
- gl2.glDrawBuffer(GLExt.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ gl2.glDrawBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
context.boundDrawBuf = rb.getSlot();
}
}
@@ -1658,7 +1667,7 @@ public class GLRenderer implements Renderer {
setFrameBuffer(fb);
if (gl2 != null) {
if (context.boundReadBuf != rb.getSlot()) {
- gl2.glReadBuffer(GLExt.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
+ gl2.glReadBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot());
context.boundReadBuf = rb.getSlot();
}
}
@@ -1682,7 +1691,7 @@ public class GLRenderer implements Renderer {
public void deleteFrameBuffer(FrameBuffer fb) {
if (fb.getId() != -1) {
if (context.boundFBO == fb.getId()) {
- glfbo.glBindFramebufferEXT(GLExt.GL_FRAMEBUFFER_EXT, 0);
+ glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0);
context.boundFBO = 0;
}
@@ -2620,32 +2629,13 @@ public class GLRenderer implements Renderer {
return;
}
- if (context.pointSprite && mesh.getMode() != Mode.Points) {
- // XXX: Hack, disable point sprite mode if mesh not in point mode
- if (context.boundTextures[0] != null) {
- if (context.boundTextureUnit != 0) {
- gl.glActiveTexture(GL.GL_TEXTURE0);
- context.boundTextureUnit = 0;
- }
- if (gl2 != null) {
- gl2.glDisable(GL2.GL_POINT_SPRITE);
- gl2.glDisable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
- }
- context.pointSprite = false;
- }
- }
- if (gl2 != null) {
- if (context.pointSize != mesh.getPointSize()) {
- gl2.glPointSize(mesh.getPointSize());
- context.pointSize = mesh.getPointSize();
- }
- }
if (context.lineWidth != mesh.getLineWidth()) {
gl.glLineWidth(mesh.getLineWidth());
context.lineWidth = mesh.getLineWidth();
}
- if(gl4!=null && mesh.getMode().equals(Mode.Patch)){
+
+ if (gl4 != null && mesh.getMode().equals(Mode.Patch)) {
gl4.glPatchParameter(mesh.getPatchVertexCount());
}
statistics.onMeshDrawn(mesh, lod, count);
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTiming.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTiming.java
new file mode 100644
index 000000000..7e6833b8b
--- /dev/null
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTiming.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2009-2015 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.renderer.opengl;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+
+public class GLTiming implements InvocationHandler {
+
+ private final Object obj;
+ private final GLTimingState state;
+
+ public GLTiming(Object obj, GLTimingState state) {
+ this.obj = obj;
+ this.state = state;
+ }
+
+ public static Object createGLTiming(Object glInterface, GLTimingState state, Class> ... glInterfaceClasses) {
+ return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
+ glInterfaceClasses,
+ new GLTiming(glInterface, state));
+ }
+
+ private static class CallTimingComparator implements Comparator> {
+ @Override
+ public int compare(Map.Entry o1, Map.Entry o2) {
+ return (int) (o2.getValue() - o1.getValue());
+ }
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ String methodName = method.getName();
+ if (methodName.equals("resetStats")) {
+ if (state.lastPrintOutTime + 1000000000 <= System.nanoTime() && state.sampleCount > 0) {
+ state.timeSpentInGL /= state.sampleCount;
+ System.out.println("--- TOTAL TIME SPENT IN GL CALLS: " + (state.timeSpentInGL/1000) + "us");
+
+ Map.Entry[] callTimes = new Map.Entry[state.callTiming.size()];
+ int i = 0;
+ for (Map.Entry callTime : state.callTiming.entrySet()) {
+ callTimes[i++] = callTime;
+ }
+ Arrays.sort(callTimes, new CallTimingComparator());
+ int limit = 10;
+ for (Map.Entry callTime : callTimes) {
+ long val = callTime.getValue() / state.sampleCount;
+ String name = callTime.getKey();
+ String pad = " ".substring(0, 30 - name.length());
+ System.out.println("\t" + callTime.getKey() + pad + (val/1000) + "us");
+ if (limit-- == 0) break;
+ }
+ for (Map.Entry callTime : callTimes) {
+ state.callTiming.put(callTime.getKey(), Long.valueOf(0));
+ }
+
+ state.sampleCount = 0;
+ state.timeSpentInGL = 0;
+ state.lastPrintOutTime = System.nanoTime();
+ } else {
+ state.sampleCount++;
+ }
+ return null;
+ } else {
+ Long currentTimeObj = state.callTiming.get(methodName);
+ long currentTime = 0;
+ if (currentTimeObj != null) currentTime = currentTimeObj;
+
+
+ long startTime = System.nanoTime();
+ Object result = method.invoke(obj, args);
+ long delta = System.nanoTime() - startTime;
+
+ currentTime += delta;
+ state.timeSpentInGL += delta;
+
+ state.callTiming.put(methodName, currentTime);
+
+ if (delta > 1000000 && !methodName.equals("glClear")) {
+ // More than 1ms
+ // Ignore glClear as it cannot be avoided.
+ System.out.println("GL call " + methodName + " took " + (delta/1000) + "us to execute!");
+ }
+
+ return result;
+ }
+ }
+
+}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTimingState.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTimingState.java
new file mode 100644
index 000000000..ca25810d4
--- /dev/null
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTimingState.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2009-2015 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.renderer.opengl;
+
+import java.util.HashMap;
+
+public class GLTimingState {
+ long timeSpentInGL = 0;
+ int sampleCount = 0;
+ long lastPrintOutTime = 0;
+ final HashMap callTiming = new HashMap();
+}
\ No newline at end of file
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
index b69d524b4..1b0d70749 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
@@ -64,6 +64,7 @@ public final class GLTracer implements InvocationHandler {
noEnumArgs("glScissor", 0, 1, 2, 3);
noEnumArgs("glClear", 0);
noEnumArgs("glGetInteger", 1);
+ noEnumArgs("glGetString", 1);
noEnumArgs("glBindTexture", 1);
noEnumArgs("glPixelStorei", 1);
@@ -95,8 +96,6 @@ public final class GLTracer implements InvocationHandler {
noEnumArgs("glFramebufferTexture2DEXT", 3, 4);
noEnumArgs("glBlitFramebufferEXT", 0, 1, 2, 3, 4, 5, 6, 7, 8);
-
-
noEnumArgs("glCreateProgram", -1);
noEnumArgs("glCreateShader", -1);
noEnumArgs("glShaderSource", 0);
@@ -155,7 +154,7 @@ public final class GLTracer implements InvocationHandler {
* @return A tracer that implements the given interface
*/
public static Object createGlesTracer(Object glInterface, Class> glInterfaceClass) {
- IntMap constMap = generateConstantMap(GL.class, GLExt.class);
+ IntMap constMap = generateConstantMap(GL.class, GLFbo.class, GLExt.class);
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
new Class>[] { glInterfaceClass },
new GLTracer(glInterface, constMap));
@@ -169,7 +168,7 @@ public final class GLTracer implements InvocationHandler {
* @return A tracer that implements the given interface
*/
public static Object createDesktopGlTracer(Object glInterface, Class> ... glInterfaceClasses) {
- IntMap constMap = generateConstantMap(GL2.class, GLExt.class);
+ IntMap constMap = generateConstantMap(GL2.class, GL3.class, GL4.class, GLFbo.class, GLExt.class);
return Proxy.newProxyInstance(glInterface.getClass().getClassLoader(),
glInterfaceClasses,
new GLTracer(glInterface, constMap));
diff --git a/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java b/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java
index ed53e89bc..a7a53b915 100644
--- a/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java
+++ b/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java
@@ -127,6 +127,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator {
unIndent();
startCondition(shaderNode.getCondition(), source);
source.append(nodeSource);
+ source.append("\n");
endCondition(shaderNode.getCondition(), source);
indent();
}
diff --git a/jme3-core/src/main/java/com/jme3/texture/Image.java b/jme3-core/src/main/java/com/jme3/texture/Image.java
index 2bbf746ff..96ab62819 100644
--- a/jme3-core/src/main/java/com/jme3/texture/Image.java
+++ b/jme3-core/src/main/java/com/jme3/texture/Image.java
@@ -421,7 +421,8 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
/**
* @return True if the image needs to have mipmaps generated
- * for it (as requested by the texture).
+ * for it (as requested by the texture). This stays true even
+ * after mipmaps have been generated.
*/
public boolean isGeneratedMipmapsRequired() {
return needGeneratedMips;
@@ -434,8 +435,9 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
@Override
public void setUpdateNeeded() {
super.setUpdateNeeded();
- if (!isGeneratedMipmapsRequired() && !hasMipmaps()) {
- setNeedGeneratedMipmaps();
+ if (isGeneratedMipmapsRequired() && !hasMipmaps()) {
+ // Mipmaps are no longer valid, since the image was changed.
+ setMipmapsGenerated(false);
}
}
@@ -527,11 +529,13 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
this();
- if (mipMapSizes != null && mipMapSizes.length <= 1) {
- mipMapSizes = null;
- } else {
- needGeneratedMips = false;
- mipsWereGenerated = true;
+ if (mipMapSizes != null) {
+ if (mipMapSizes.length <= 1) {
+ mipMapSizes = null;
+ } else {
+ needGeneratedMips = false;
+ mipsWereGenerated = true;
+ }
}
setFormat(format);
@@ -787,8 +791,8 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
needGeneratedMips = false;
mipsWereGenerated = false;
} else {
- needGeneratedMips = false;
- mipsWereGenerated = true;
+ needGeneratedMips = true;
+ mipsWereGenerated = false;
}
setUpdateNeeded();
diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
index 5d207cf83..26b68a533 100644
--- a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
+++ b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
@@ -72,17 +72,19 @@ uniform float m_Shininess;
#endif
void main(){
- #ifdef NORMALMAP
- mat3 tbnMat = mat3(normalize(vTangent.xyz) , normalize(vBinormal.xyz) , normalize(vNormal.xyz));
+ #if !defined(VERTEX_LIGHTING)
+ #if defined(NORMALMAP)
+ mat3 tbnMat = mat3(normalize(vTangent.xyz) , normalize(vBinormal.xyz) , normalize(vNormal.xyz));
- if (!gl_FrontFacing)
- {
- tbnMat[2] = -tbnMat[2];
- }
+ if (!gl_FrontFacing)
+ {
+ tbnMat[2] = -tbnMat[2];
+ }
- vec3 viewDir = normalize(-vPos.xyz * tbnMat);
- #else
- vec3 viewDir = normalize(-vPos.xyz);
+ vec3 viewDir = normalize(-vPos.xyz * tbnMat);
+ #else
+ vec3 viewDir = normalize(-vPos.xyz);
+ #endif
#endif
vec2 newTexCoord;
diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
index 62b206b7e..6ad224d9b 100644
--- a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
+++ b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
@@ -45,6 +45,9 @@ attribute vec3 inNormal;
#else
varying vec3 specularAccum;
varying vec4 diffuseAccum;
+ #ifdef COLORRAMP
+ uniform sampler2D m_ColorRamp;
+ #endif
#endif
#ifdef USE_REFLECTION
@@ -160,14 +163,14 @@ void main(){
#if __VERSION__ >= 110
}
#endif
- vec2 v = computeLighting(wvNormal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess);
+ vec2 light = computeLighting(wvNormal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess);
#ifdef COLORRAMP
- diffuseAccum += texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb * diffuseColor;
- specularAccum += texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb * specularColor;
+ diffuseAccum.rgb += texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb * diffuseColor.rgb;
+ specularAccum.rgb += texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb * specularColor;
#else
- diffuseAccum += v.x * diffuseColor;
- specularAccum += v.y * specularColor;
+ diffuseAccum.rgb += light.x * diffuseColor.rgb;
+ specularAccum.rgb += light.y * specularColor;
#endif
}
#endif
diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
index 5d7f5ca8c..0c81c3e14 100644
--- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
+++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
@@ -569,10 +569,15 @@ public class J3MLoader implements AssetLoader {
public Object load(AssetInfo info) throws IOException {
this.assetManager = info.getManager();
-
+
InputStream in = info.openStream();
try {
- key = info.getKey();
+ key = info.getKey();
+ if (key.getExtension().equals("j3m") && !(key instanceof MaterialKey)) {
+ throw new IOException("Material instances must be loaded via MaterialKey");
+ } else if (key.getExtension().equals("j3md") && key instanceof MaterialKey) {
+ throw new IOException("Material definitions must be loaded via AssetKey");
+ }
loadFromRoot(BlockLanguageParser.parse(in));
} finally {
if (in != null){
@@ -581,9 +586,6 @@ public class J3MLoader implements AssetLoader {
}
if (material != null){
- if (!(info.getKey() instanceof MaterialKey)){
- throw new IOException("Material instances must be loaded via MaterialKey");
- }
// material implementation
return material;
}else{
diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java
index 9c71aa132..726cf3e19 100644
--- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java
+++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java
@@ -242,21 +242,12 @@ public class DXTFlipper {
img.position(blockByteOffset);
img.limit(blockByteOffset + bpb);
- img.get(colorBlock);
- if (type == 4 || type == 5)
- flipDXT5Block(colorBlock, h);
- else
- flipDXT1orDXTA3Block(colorBlock, h);
-
- // write block (no need to flip block indexes, only pixels
- // inside block
- retImg.put(colorBlock);
-
if (alphaBlock != null){
img.get(alphaBlock);
switch (type){
case 2:
- flipDXT3Block(alphaBlock, h); break;
+ flipDXT3Block(alphaBlock, h);
+ break;
case 3:
case 4:
flipDXT5Block(alphaBlock, h);
@@ -264,6 +255,16 @@ public class DXTFlipper {
}
retImg.put(alphaBlock);
}
+
+ img.get(colorBlock);
+ if (type == 4 || type == 5)
+ flipDXT5Block(colorBlock, h);
+ else
+ flipDXT1orDXTA3Block(colorBlock, h);
+
+ // write block (no need to flip block indexes, only pixels
+ // inside block
+ retImg.put(colorBlock);
}
retImg.rewind();
}else if (h >= 4){
diff --git a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java
index 75d6b1c86..7e9012b6d 100644
--- a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java
+++ b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java
@@ -162,8 +162,8 @@ public class SSAOFilter extends Filter {
};
ssaoPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth, 1, ssaoMat);
- ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear);
- ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear);
+// ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear);
+// ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear);
postRenderPasses.add(ssaoPass);
material = new Material(manager, "Common/MatDefs/SSAO/ssaoBlur.j3md");
material.setTexture("SSAOMap", ssaoPass.getRenderedTexture());
diff --git a/jme3-ios/src/main/java/com/jme3/asset/IOS.cfg b/jme3-ios/src/main/java/com/jme3/asset/IOS.cfg
index 715eab985..e9d79a459 100644
--- a/jme3-ios/src/main/java/com/jme3/asset/IOS.cfg
+++ b/jme3-ios/src/main/java/com/jme3/asset/IOS.cfg
@@ -2,3 +2,9 @@ INCLUDE com/jme3/asset/General.cfg
# IOS specific loaders
LOADER com.jme3.system.ios.IosImageLoader : jpg, bmp, gif, png, jpeg
+LOADER com.jme3.audio.plugins.OGGLoader : ogg
+LOADER com.jme3.material.plugins.J3MLoader : j3m
+LOADER com.jme3.material.plugins.J3MLoader : j3md
+LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib
+LOADER com.jme3.export.binary.BinaryImporter : j3o
+LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
diff --git a/jme3-ios/src/main/java/com/jme3/audio/android/AL.java b/jme3-ios/src/main/java/com/jme3/audio/android/AL.java
deleted file mode 100644
index d8fea3933..000000000
--- a/jme3-ios/src/main/java/com/jme3/audio/android/AL.java
+++ /dev/null
@@ -1,1054 +0,0 @@
-package com.jme3.audio.android;
-
-/**
- *
- * @author iwgeric
- */
-public class AL {
-
-
-
- /* ********** */
- /* FROM ALC.h */
- /* ********** */
-
-// typedef struct ALCdevice_struct ALCdevice;
-// typedef struct ALCcontext_struct ALCcontext;
-
-
- /**
- * No error
- */
- static final int ALC_NO_ERROR = 0;
-
- /**
- * No device
- */
- static final int ALC_INVALID_DEVICE = 0xA001;
-
- /**
- * invalid context ID
- */
- static final int ALC_INVALID_CONTEXT = 0xA002;
-
- /**
- * bad enum
- */
- static final int ALC_INVALID_ENUM = 0xA003;
-
- /**
- * bad value
- */
- static final int ALC_INVALID_VALUE = 0xA004;
-
- /**
- * Out of memory.
- */
- static final int ALC_OUT_OF_MEMORY = 0xA005;
-
-
- /**
- * The Specifier string for default device
- */
- static final int ALC_DEFAULT_DEVICE_SPECIFIER = 0x1004;
- static final int ALC_DEVICE_SPECIFIER = 0x1005;
- static final int ALC_EXTENSIONS = 0x1006;
-
- static final int ALC_MAJOR_VERSION = 0x1000;
- static final int ALC_MINOR_VERSION = 0x1001;
-
- static final int ALC_ATTRIBUTES_SIZE = 0x1002;
- static final int ALC_ALL_ATTRIBUTES = 0x1003;
-
-
- /**
- * Capture extension
- */
- static final int ALC_EXT_CAPTURE = 1;
- static final int ALC_CAPTURE_DEVICE_SPECIFIER = 0x310;
- static final int ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER = 0x311;
- static final int ALC_CAPTURE_SAMPLES = 0x312;
-
-
- /**
- * ALC_ENUMERATE_ALL_EXT enums
- */
- static final int ALC_ENUMERATE_ALL_EXT = 1;
- static final int ALC_DEFAULT_ALL_DEVICES_SPECIFIER = 0x1012;
- static final int ALC_ALL_DEVICES_SPECIFIER = 0x1013;
-
-
- /* ********** */
- /* FROM AL.h */
- /* ********** */
-
-/** Boolean False. */
- static final int AL_FALSE = 0;
-
-/** Boolean True. */
- static final int AL_TRUE = 1;
-
-/* "no distance model" or "no buffer" */
- static final int AL_NONE = 0;
-
-/** Indicate Source has relative coordinates. */
- static final int AL_SOURCE_RELATIVE = 0x202;
-
-
-
-/**
- * Directional source, inner cone angle, in degrees.
- * Range: [0-360]
- * Default: 360
- */
- static final int AL_CONE_INNER_ANGLE = 0x1001;
-
-/**
- * Directional source, outer cone angle, in degrees.
- * Range: [0-360]
- * Default: 360
- */
- static final int AL_CONE_OUTER_ANGLE = 0x1002;
-
-/**
- * Specify the pitch to be applied at source.
- * Range: [0.5-2.0]
- * Default: 1.0
- */
- static final int AL_PITCH = 0x1003;
-
-/**
- * Specify the current location in three dimensional space.
- * OpenAL, like OpenGL, uses a right handed coordinate system,
- * where in a frontal default view X (thumb) points right,
- * Y points up (index finger), and Z points towards the
- * viewer/camera (middle finger).
- * To switch from a left handed coordinate system, flip the
- * sign on the Z coordinate.
- * Listener position is always in the world coordinate system.
- */
- static final int AL_POSITION = 0x1004;
-
-/** Specify the current direction. */
- static final int AL_DIRECTION = 0x1005;
-
-/** Specify the current velocity in three dimensional space. */
- static final int AL_VELOCITY = 0x1006;
-
-/**
- * Indicate whether source is looping.
- * Type: ALboolean?
- * Range: [AL_TRUE, AL_FALSE]
- * Default: FALSE.
- */
- static final int AL_LOOPING = 0x1007;
-
-/**
- * Indicate the buffer to provide sound samples.
- * Type: ALuint.
- * Range: any valid Buffer id.
- */
- static final int AL_BUFFER = 0x1009;
-
-/**
- * Indicate the gain (volume amplification) applied.
- * Type: ALfloat.
- * Range: ]0.0- ]
- * A value of 1.0 means un-attenuated/unchanged.
- * Each division by 2 equals an attenuation of -6dB.
- * Each multiplicaton with 2 equals an amplification of +6dB.
- * A value of 0.0 is meaningless with respect to a logarithmic
- * scale; it is interpreted as zero volume - the channel
- * is effectively disabled.
- */
- static final int AL_GAIN = 0x100A;
-
-/*
- * Indicate minimum source attenuation
- * Type: ALfloat
- * Range: [0.0 - 1.0]
- *
- * Logarthmic
- */
- static final int AL_MIN_GAIN = 0x100D;
-
-/**
- * Indicate maximum source attenuation
- * Type: ALfloat
- * Range: [0.0 - 1.0]
- *
- * Logarthmic
- */
- static final int AL_MAX_GAIN = 0x100E;
-
-/**
- * Indicate listener orientation.
- *
- * at/up
- */
- static final int AL_ORIENTATION = 0x100F;
-
-/**
- * Source state information.
- */
- static final int AL_SOURCE_STATE = 0x1010;
- static final int AL_INITIAL = 0x1011;
- static final int AL_PLAYING = 0x1012;
- static final int AL_PAUSED = 0x1013;
- static final int AL_STOPPED = 0x1014;
-
-/**
- * Buffer Queue params
- */
- static final int AL_BUFFERS_QUEUED = 0x1015;
- static final int AL_BUFFERS_PROCESSED = 0x1016;
-
-/**
- * Source buffer position information
- */
- static final int AL_SEC_OFFSET = 0x1024;
- static final int AL_SAMPLE_OFFSET = 0x1025;
- static final int AL_BYTE_OFFSET = 0x1026;
-
-/*
- * Source type (Static, Streaming or undetermined)
- * Source is Static if a Buffer has been attached using AL_BUFFER
- * Source is Streaming if one or more Buffers have been attached using alSourceQueueBuffers
- * Source is undetermined when it has the NULL buffer attached
- */
- static final int AL_SOURCE_TYPE = 0x1027;
- static final int AL_STATIC = 0x1028;
- static final int AL_STREAMING = 0x1029;
- static final int AL_UNDETERMINED = 0x1030;
-
-/** Sound samples: format specifier. */
- static final int AL_FORMAT_MONO8 = 0x1100;
- static final int AL_FORMAT_MONO16 = 0x1101;
- static final int AL_FORMAT_STEREO8 = 0x1102;
- static final int AL_FORMAT_STEREO16 = 0x1103;
-
-/**
- * source specific reference distance
- * Type: ALfloat
- * Range: 0.0 - +inf
- *
- * At 0.0, no distance attenuation occurs. Default is
- * 1.0.
- */
- static final int AL_REFERENCE_DISTANCE = 0x1020;
-
-/**
- * source specific rolloff factor
- * Type: ALfloat
- * Range: 0.0 - +inf
- *
- */
- static final int AL_ROLLOFF_FACTOR = 0x1021;
-
-/**
- * Directional source, outer cone gain.
- *
- * Default: 0.0
- * Range: [0.0 - 1.0]
- * Logarithmic
- */
- static final int AL_CONE_OUTER_GAIN = 0x1022;
-
-/**
- * Indicate distance above which sources are not
- * attenuated using the inverse clamped distance model.
- *
- * Default: +inf
- * Type: ALfloat
- * Range: 0.0 - +inf
- */
- static final int AL_MAX_DISTANCE = 0x1023;
-
-/**
- * Sound samples: frequency, in units of Hertz [Hz].
- * This is the number of samples per second. Half of the
- * sample frequency marks the maximum significant
- * frequency component.
- */
- static final int AL_FREQUENCY = 0x2001;
- static final int AL_BITS = 0x2002;
- static final int AL_CHANNELS = 0x2003;
- static final int AL_SIZE = 0x2004;
-
-/**
- * Buffer state.
- *
- * Not supported for public use (yet).
- */
- static final int AL_UNUSED = 0x2010;
- static final int AL_PENDING = 0x2011;
- static final int AL_PROCESSED = 0x2012;
-
-
-/** Errors: No Error. */
- static final int AL_NO_ERROR = 0;
-
-/**
- * Invalid Name paramater passed to AL call.
- */
- static final int AL_INVALID_NAME = 0xA001;
-
-/**
- * Invalid parameter passed to AL call.
- */
- static final int AL_INVALID_ENUM = 0xA002;
-
-/**
- * Invalid enum parameter value.
- */
- static final int AL_INVALID_VALUE = 0xA003;
-
-/**
- * Illegal call.
- */
- static final int AL_INVALID_OPERATION = 0xA004;
-
-
-/**
- * No mojo.
- */
- static final int AL_OUT_OF_MEMORY = 0xA005;
-
-
-/** Context strings: Vendor Name. */
- static final int AL_VENDOR = 0xB001;
- static final int AL_VERSION = 0xB002;
- static final int AL_RENDERER = 0xB003;
- static final int AL_EXTENSIONS = 0xB004;
-
-/** Global tweakage. */
-
-/**
- * Doppler scale. Default 1.0
- */
- static final int AL_DOPPLER_FACTOR = 0xC000;
-
-/**
- * Tweaks speed of propagation.
- */
- static final int AL_DOPPLER_VELOCITY = 0xC001;
-
-/**
- * Speed of Sound in units per second
- */
- static final int AL_SPEED_OF_SOUND = 0xC003;
-
-/**
- * Distance models
- *
- * used in conjunction with DistanceModel
- *
- * implicit: NONE, which disances distance attenuation.
- */
- static final int AL_DISTANCE_MODEL = 0xD000;
- static final int AL_INVERSE_DISTANCE = 0xD001;
- static final int AL_INVERSE_DISTANCE_CLAMPED = 0xD002;
- static final int AL_LINEAR_DISTANCE = 0xD003;
- static final int AL_LINEAR_DISTANCE_CLAMPED = 0xD004;
- static final int AL_EXPONENT_DISTANCE = 0xD005;
- static final int AL_EXPONENT_DISTANCE_CLAMPED = 0xD006;
-
- /* ********** */
- /* FROM efx.h */
- /* ********** */
-
- static final String ALC_EXT_EFX_NAME = "ALC_EXT_EFX";
-
- static final int ALC_EFX_MAJOR_VERSION = 0x20001;
- static final int ALC_EFX_MINOR_VERSION = 0x20002;
- static final int ALC_MAX_AUXILIARY_SENDS = 0x20003;
-
-
-///* Listener properties. */
-//#define AL_METERS_PER_UNIT 0x20004
-//
-///* Source properties. */
- static final int AL_DIRECT_FILTER = 0x20005;
- static final int AL_AUXILIARY_SEND_FILTER = 0x20006;
-//#define AL_AIR_ABSORPTION_FACTOR 0x20007
-//#define AL_ROOM_ROLLOFF_FACTOR 0x20008
-//#define AL_CONE_OUTER_GAINHF 0x20009
- static final int AL_DIRECT_FILTER_GAINHF_AUTO = 0x2000A;
-//#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B
-//#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C
-//
-//
-///* Effect properties. */
-//
-///* Reverb effect parameters */
- static final int AL_REVERB_DENSITY = 0x0001;
- static final int AL_REVERB_DIFFUSION = 0x0002;
- static final int AL_REVERB_GAIN = 0x0003;
- static final int AL_REVERB_GAINHF = 0x0004;
- static final int AL_REVERB_DECAY_TIME = 0x0005;
- static final int AL_REVERB_DECAY_HFRATIO = 0x0006;
- static final int AL_REVERB_REFLECTIONS_GAIN = 0x0007;
- static final int AL_REVERB_REFLECTIONS_DELAY = 0x0008;
- static final int AL_REVERB_LATE_REVERB_GAIN = 0x0009;
- static final int AL_REVERB_LATE_REVERB_DELAY = 0x000A;
- static final int AL_REVERB_AIR_ABSORPTION_GAINHF = 0x000B;
- static final int AL_REVERB_ROOM_ROLLOFF_FACTOR = 0x000C;
- static final int AL_REVERB_DECAY_HFLIMIT = 0x000D;
-
-///* EAX Reverb effect parameters */
-//#define AL_EAXREVERB_DENSITY 0x0001
-//#define AL_EAXREVERB_DIFFUSION 0x0002
-//#define AL_EAXREVERB_GAIN 0x0003
-//#define AL_EAXREVERB_GAINHF 0x0004
-//#define AL_EAXREVERB_GAINLF 0x0005
-//#define AL_EAXREVERB_DECAY_TIME 0x0006
-//#define AL_EAXREVERB_DECAY_HFRATIO 0x0007
-//#define AL_EAXREVERB_DECAY_LFRATIO 0x0008
-//#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009
-//#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A
-//#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B
-//#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C
-//#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D
-//#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E
-//#define AL_EAXREVERB_ECHO_TIME 0x000F
-//#define AL_EAXREVERB_ECHO_DEPTH 0x0010
-//#define AL_EAXREVERB_MODULATION_TIME 0x0011
-//#define AL_EAXREVERB_MODULATION_DEPTH 0x0012
-//#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013
-//#define AL_EAXREVERB_HFREFERENCE 0x0014
-//#define AL_EAXREVERB_LFREFERENCE 0x0015
-//#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016
-//#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017
-//
-///* Chorus effect parameters */
-//#define AL_CHORUS_WAVEFORM 0x0001
-//#define AL_CHORUS_PHASE 0x0002
-//#define AL_CHORUS_RATE 0x0003
-//#define AL_CHORUS_DEPTH 0x0004
-//#define AL_CHORUS_FEEDBACK 0x0005
-//#define AL_CHORUS_DELAY 0x0006
-//
-///* Distortion effect parameters */
-//#define AL_DISTORTION_EDGE 0x0001
-//#define AL_DISTORTION_GAIN 0x0002
-//#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003
-//#define AL_DISTORTION_EQCENTER 0x0004
-//#define AL_DISTORTION_EQBANDWIDTH 0x0005
-//
-///* Echo effect parameters */
-//#define AL_ECHO_DELAY 0x0001
-//#define AL_ECHO_LRDELAY 0x0002
-//#define AL_ECHO_DAMPING 0x0003
-//#define AL_ECHO_FEEDBACK 0x0004
-//#define AL_ECHO_SPREAD 0x0005
-//
-///* Flanger effect parameters */
-//#define AL_FLANGER_WAVEFORM 0x0001
-//#define AL_FLANGER_PHASE 0x0002
-//#define AL_FLANGER_RATE 0x0003
-//#define AL_FLANGER_DEPTH 0x0004
-//#define AL_FLANGER_FEEDBACK 0x0005
-//#define AL_FLANGER_DELAY 0x0006
-//
-///* Frequency shifter effect parameters */
-//#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001
-//#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002
-//#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003
-//
-///* Vocal morpher effect parameters */
-//#define AL_VOCAL_MORPHER_PHONEMEA 0x0001
-//#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002
-//#define AL_VOCAL_MORPHER_PHONEMEB 0x0003
-//#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004
-//#define AL_VOCAL_MORPHER_WAVEFORM 0x0005
-//#define AL_VOCAL_MORPHER_RATE 0x0006
-//
-///* Pitchshifter effect parameters */
-//#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001
-//#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002
-//
-///* Ringmodulator effect parameters */
-//#define AL_RING_MODULATOR_FREQUENCY 0x0001
-//#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002
-//#define AL_RING_MODULATOR_WAVEFORM 0x0003
-//
-///* Autowah effect parameters */
-//#define AL_AUTOWAH_ATTACK_TIME 0x0001
-//#define AL_AUTOWAH_RELEASE_TIME 0x0002
-//#define AL_AUTOWAH_RESONANCE 0x0003
-//#define AL_AUTOWAH_PEAK_GAIN 0x0004
-//
-///* Compressor effect parameters */
-//#define AL_COMPRESSOR_ONOFF 0x0001
-//
-///* Equalizer effect parameters */
-//#define AL_EQUALIZER_LOW_GAIN 0x0001
-//#define AL_EQUALIZER_LOW_CUTOFF 0x0002
-//#define AL_EQUALIZER_MID1_GAIN 0x0003
-//#define AL_EQUALIZER_MID1_CENTER 0x0004
-//#define AL_EQUALIZER_MID1_WIDTH 0x0005
-//#define AL_EQUALIZER_MID2_GAIN 0x0006
-//#define AL_EQUALIZER_MID2_CENTER 0x0007
-//#define AL_EQUALIZER_MID2_WIDTH 0x0008
-//#define AL_EQUALIZER_HIGH_GAIN 0x0009
-//#define AL_EQUALIZER_HIGH_CUTOFF 0x000A
-//
-///* Effect type */
-//#define AL_EFFECT_FIRST_PARAMETER 0x0000
-//#define AL_EFFECT_LAST_PARAMETER 0x8000
- static final int AL_EFFECT_TYPE = 0x8001;
-//
-///* Effect types, used with the AL_EFFECT_TYPE property */
-//#define AL_EFFECT_NULL 0x0000
- static final int AL_EFFECT_REVERB = 0x0001;
-//#define AL_EFFECT_CHORUS 0x0002
-//#define AL_EFFECT_DISTORTION 0x0003
-//#define AL_EFFECT_ECHO 0x0004
-//#define AL_EFFECT_FLANGER 0x0005
-//#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006
-//#define AL_EFFECT_VOCAL_MORPHER 0x0007
-//#define AL_EFFECT_PITCH_SHIFTER 0x0008
-//#define AL_EFFECT_RING_MODULATOR 0x0009
-//#define AL_EFFECT_AUTOWAH 0x000A
-//#define AL_EFFECT_COMPRESSOR 0x000B
-//#define AL_EFFECT_EQUALIZER 0x000C
-//#define AL_EFFECT_EAXREVERB 0x8000
-//
-///* Auxiliary Effect Slot properties. */
- static final int AL_EFFECTSLOT_EFFECT = 0x0001;
-//#define AL_EFFECTSLOT_GAIN 0x0002
-//#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003
-//
-///* NULL Auxiliary Slot ID to disable a source send. */
-//#define AL_EFFECTSLOT_NULL 0x0000
-//
-//
-///* Filter properties. */
-//
-///* Lowpass filter parameters */
- static final int AL_LOWPASS_GAIN = 0x0001;
- static final int AL_LOWPASS_GAINHF = 0x0002;
-//
-///* Highpass filter parameters */
-//#define AL_HIGHPASS_GAIN 0x0001
-//#define AL_HIGHPASS_GAINLF 0x0002
-//
-///* Bandpass filter parameters */
-//#define AL_BANDPASS_GAIN 0x0001
-//#define AL_BANDPASS_GAINLF 0x0002
-//#define AL_BANDPASS_GAINHF 0x0003
-//
-///* Filter type */
-//#define AL_FILTER_FIRST_PARAMETER 0x0000
-//#define AL_FILTER_LAST_PARAMETER 0x8000
- static final int AL_FILTER_TYPE = 0x8001;
-//
-///* Filter types, used with the AL_FILTER_TYPE property */
- static final int AL_FILTER_NULL = 0x0000;
- static final int AL_FILTER_LOWPASS = 0x0001;
- static final int AL_FILTER_HIGHPASS = 0x0002;
-//#define AL_FILTER_BANDPASS 0x0003
-//
-///* Filter ranges and defaults. */
-//
-///* Lowpass filter */
-//#define AL_LOWPASS_MIN_GAIN (0.0f)
-//#define AL_LOWPASS_MAX_GAIN (1.0f)
-//#define AL_LOWPASS_DEFAULT_GAIN (1.0f)
-//
-//#define AL_LOWPASS_MIN_GAINHF (0.0f)
-//#define AL_LOWPASS_MAX_GAINHF (1.0f)
-//#define AL_LOWPASS_DEFAULT_GAINHF (1.0f)
-//
-///* Highpass filter */
-//#define AL_HIGHPASS_MIN_GAIN (0.0f)
-//#define AL_HIGHPASS_MAX_GAIN (1.0f)
-//#define AL_HIGHPASS_DEFAULT_GAIN (1.0f)
-//
-//#define AL_HIGHPASS_MIN_GAINLF (0.0f)
-//#define AL_HIGHPASS_MAX_GAINLF (1.0f)
-//#define AL_HIGHPASS_DEFAULT_GAINLF (1.0f)
-//
-///* Bandpass filter */
-//#define AL_BANDPASS_MIN_GAIN (0.0f)
-//#define AL_BANDPASS_MAX_GAIN (1.0f)
-//#define AL_BANDPASS_DEFAULT_GAIN (1.0f)
-//
-//#define AL_BANDPASS_MIN_GAINHF (0.0f)
-//#define AL_BANDPASS_MAX_GAINHF (1.0f)
-//#define AL_BANDPASS_DEFAULT_GAINHF (1.0f)
-//
-//#define AL_BANDPASS_MIN_GAINLF (0.0f)
-//#define AL_BANDPASS_MAX_GAINLF (1.0f)
-//#define AL_BANDPASS_DEFAULT_GAINLF (1.0f)
-//
-//
-///* Effect parameter ranges and defaults. */
-//
-///* Standard reverb effect */
-//#define AL_REVERB_MIN_DENSITY (0.0f)
-//#define AL_REVERB_MAX_DENSITY (1.0f)
-//#define AL_REVERB_DEFAULT_DENSITY (1.0f)
-//
-//#define AL_REVERB_MIN_DIFFUSION (0.0f)
-//#define AL_REVERB_MAX_DIFFUSION (1.0f)
-//#define AL_REVERB_DEFAULT_DIFFUSION (1.0f)
-//
-//#define AL_REVERB_MIN_GAIN (0.0f)
-//#define AL_REVERB_MAX_GAIN (1.0f)
-//#define AL_REVERB_DEFAULT_GAIN (0.32f)
-//
-//#define AL_REVERB_MIN_GAINHF (0.0f)
-//#define AL_REVERB_MAX_GAINHF (1.0f)
-//#define AL_REVERB_DEFAULT_GAINHF (0.89f)
-//
-//#define AL_REVERB_MIN_DECAY_TIME (0.1f)
-//#define AL_REVERB_MAX_DECAY_TIME (20.0f)
-//#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f)
-//
-//#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f)
-//#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f)
-//#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f)
-//
-//#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f)
-//#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f)
-//#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
-//
-//#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f)
-//#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f)
-//#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
-//
-//#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f)
-//#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f)
-//#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
-//
-//#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f)
-//#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f)
-//#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
-//
-//#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
-//#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
-//#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
-//
-//#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
-//#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
-//#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
-//
-//#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE
-//#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE
-//#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE
-//
-///* EAX reverb effect */
-//#define AL_EAXREVERB_MIN_DENSITY (0.0f)
-//#define AL_EAXREVERB_MAX_DENSITY (1.0f)
-//#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f)
-//
-//#define AL_EAXREVERB_MIN_DIFFUSION (0.0f)
-//#define AL_EAXREVERB_MAX_DIFFUSION (1.0f)
-//#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f)
-//
-//#define AL_EAXREVERB_MIN_GAIN (0.0f)
-//#define AL_EAXREVERB_MAX_GAIN (1.0f)
-//#define AL_EAXREVERB_DEFAULT_GAIN (0.32f)
-//
-//#define AL_EAXREVERB_MIN_GAINHF (0.0f)
-//#define AL_EAXREVERB_MAX_GAINHF (1.0f)
-//#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f)
-//
-//#define AL_EAXREVERB_MIN_GAINLF (0.0f)
-//#define AL_EAXREVERB_MAX_GAINLF (1.0f)
-//#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f)
-//#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f)
-//#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f)
-//#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f)
-//#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f)
-//#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f)
-//#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f)
-//
-//#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f)
-//#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f)
-//#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f)
-//
-//#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f)
-//#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f)
-//#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f)
-//
-//#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f)
-//
-//#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f)
-//#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f)
-//#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f)
-//
-//#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f)
-//#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f)
-//#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f)
-//
-//#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f)
-//
-//#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f)
-//#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f)
-//#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f)
-//
-//#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f)
-//#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f)
-//#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f)
-//
-//#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f)
-//#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f)
-//#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f)
-//
-//#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f)
-//#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f)
-//#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f)
-//
-//#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f)
-//#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f)
-//#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f)
-//
-//#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f)
-//#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f)
-//#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f)
-//
-//#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f)
-//#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f)
-//#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f)
-//
-//#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
-//#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
-//#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
-//
-//#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE
-//#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE
-//#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE
-//
-///* Chorus effect */
-//#define AL_CHORUS_WAVEFORM_SINUSOID (0)
-//#define AL_CHORUS_WAVEFORM_TRIANGLE (1)
-//
-//#define AL_CHORUS_MIN_WAVEFORM (0)
-//#define AL_CHORUS_MAX_WAVEFORM (1)
-//#define AL_CHORUS_DEFAULT_WAVEFORM (1)
-//
-//#define AL_CHORUS_MIN_PHASE (-180)
-//#define AL_CHORUS_MAX_PHASE (180)
-//#define AL_CHORUS_DEFAULT_PHASE (90)
-//
-//#define AL_CHORUS_MIN_RATE (0.0f)
-//#define AL_CHORUS_MAX_RATE (10.0f)
-//#define AL_CHORUS_DEFAULT_RATE (1.1f)
-//
-//#define AL_CHORUS_MIN_DEPTH (0.0f)
-//#define AL_CHORUS_MAX_DEPTH (1.0f)
-//#define AL_CHORUS_DEFAULT_DEPTH (0.1f)
-//
-//#define AL_CHORUS_MIN_FEEDBACK (-1.0f)
-//#define AL_CHORUS_MAX_FEEDBACK (1.0f)
-//#define AL_CHORUS_DEFAULT_FEEDBACK (0.25f)
-//
-//#define AL_CHORUS_MIN_DELAY (0.0f)
-//#define AL_CHORUS_MAX_DELAY (0.016f)
-//#define AL_CHORUS_DEFAULT_DELAY (0.016f)
-//
-///* Distortion effect */
-//#define AL_DISTORTION_MIN_EDGE (0.0f)
-//#define AL_DISTORTION_MAX_EDGE (1.0f)
-//#define AL_DISTORTION_DEFAULT_EDGE (0.2f)
-//
-//#define AL_DISTORTION_MIN_GAIN (0.01f)
-//#define AL_DISTORTION_MAX_GAIN (1.0f)
-//#define AL_DISTORTION_DEFAULT_GAIN (0.05f)
-//
-//#define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f)
-//#define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f)
-//#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f)
-//
-//#define AL_DISTORTION_MIN_EQCENTER (80.0f)
-//#define AL_DISTORTION_MAX_EQCENTER (24000.0f)
-//#define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f)
-//
-//#define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f)
-//#define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f)
-//#define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f)
-//
-///* Echo effect */
-//#define AL_ECHO_MIN_DELAY (0.0f)
-//#define AL_ECHO_MAX_DELAY (0.207f)
-//#define AL_ECHO_DEFAULT_DELAY (0.1f)
-//
-//#define AL_ECHO_MIN_LRDELAY (0.0f)
-//#define AL_ECHO_MAX_LRDELAY (0.404f)
-//#define AL_ECHO_DEFAULT_LRDELAY (0.1f)
-//
-//#define AL_ECHO_MIN_DAMPING (0.0f)
-//#define AL_ECHO_MAX_DAMPING (0.99f)
-//#define AL_ECHO_DEFAULT_DAMPING (0.5f)
-//
-//#define AL_ECHO_MIN_FEEDBACK (0.0f)
-//#define AL_ECHO_MAX_FEEDBACK (1.0f)
-//#define AL_ECHO_DEFAULT_FEEDBACK (0.5f)
-//
-//#define AL_ECHO_MIN_SPREAD (-1.0f)
-//#define AL_ECHO_MAX_SPREAD (1.0f)
-//#define AL_ECHO_DEFAULT_SPREAD (-1.0f)
-//
-///* Flanger effect */
-//#define AL_FLANGER_WAVEFORM_SINUSOID (0)
-//#define AL_FLANGER_WAVEFORM_TRIANGLE (1)
-//
-//#define AL_FLANGER_MIN_WAVEFORM (0)
-//#define AL_FLANGER_MAX_WAVEFORM (1)
-//#define AL_FLANGER_DEFAULT_WAVEFORM (1)
-//
-//#define AL_FLANGER_MIN_PHASE (-180)
-//#define AL_FLANGER_MAX_PHASE (180)
-//#define AL_FLANGER_DEFAULT_PHASE (0)
-//
-//#define AL_FLANGER_MIN_RATE (0.0f)
-//#define AL_FLANGER_MAX_RATE (10.0f)
-//#define AL_FLANGER_DEFAULT_RATE (0.27f)
-//
-//#define AL_FLANGER_MIN_DEPTH (0.0f)
-//#define AL_FLANGER_MAX_DEPTH (1.0f)
-//#define AL_FLANGER_DEFAULT_DEPTH (1.0f)
-//
-//#define AL_FLANGER_MIN_FEEDBACK (-1.0f)
-//#define AL_FLANGER_MAX_FEEDBACK (1.0f)
-//#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f)
-//
-//#define AL_FLANGER_MIN_DELAY (0.0f)
-//#define AL_FLANGER_MAX_DELAY (0.004f)
-//#define AL_FLANGER_DEFAULT_DELAY (0.002f)
-//
-///* Frequency shifter effect */
-//#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f)
-//#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f)
-//#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f)
-//
-//#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0)
-//#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2)
-//#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0)
-//
-//#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0)
-//#define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1)
-//#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2)
-//
-//#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0)
-//#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2)
-//#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0)
-//
-///* Vocal morpher effect */
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEA (0)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEA (29)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0)
-//
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0)
-//
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEB (0)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEB (29)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10)
-//
-//#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24)
-//#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24)
-//#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0)
-//
-//#define AL_VOCAL_MORPHER_PHONEME_A (0)
-//#define AL_VOCAL_MORPHER_PHONEME_E (1)
-//#define AL_VOCAL_MORPHER_PHONEME_I (2)
-//#define AL_VOCAL_MORPHER_PHONEME_O (3)
-//#define AL_VOCAL_MORPHER_PHONEME_U (4)
-//#define AL_VOCAL_MORPHER_PHONEME_AA (5)
-//#define AL_VOCAL_MORPHER_PHONEME_AE (6)
-//#define AL_VOCAL_MORPHER_PHONEME_AH (7)
-//#define AL_VOCAL_MORPHER_PHONEME_AO (8)
-//#define AL_VOCAL_MORPHER_PHONEME_EH (9)
-//#define AL_VOCAL_MORPHER_PHONEME_ER (10)
-//#define AL_VOCAL_MORPHER_PHONEME_IH (11)
-//#define AL_VOCAL_MORPHER_PHONEME_IY (12)
-//#define AL_VOCAL_MORPHER_PHONEME_UH (13)
-//#define AL_VOCAL_MORPHER_PHONEME_UW (14)
-//#define AL_VOCAL_MORPHER_PHONEME_B (15)
-//#define AL_VOCAL_MORPHER_PHONEME_D (16)
-//#define AL_VOCAL_MORPHER_PHONEME_F (17)
-//#define AL_VOCAL_MORPHER_PHONEME_G (18)
-//#define AL_VOCAL_MORPHER_PHONEME_J (19)
-//#define AL_VOCAL_MORPHER_PHONEME_K (20)
-//#define AL_VOCAL_MORPHER_PHONEME_L (21)
-//#define AL_VOCAL_MORPHER_PHONEME_M (22)
-//#define AL_VOCAL_MORPHER_PHONEME_N (23)
-//#define AL_VOCAL_MORPHER_PHONEME_P (24)
-//#define AL_VOCAL_MORPHER_PHONEME_R (25)
-//#define AL_VOCAL_MORPHER_PHONEME_S (26)
-//#define AL_VOCAL_MORPHER_PHONEME_T (27)
-//#define AL_VOCAL_MORPHER_PHONEME_V (28)
-//#define AL_VOCAL_MORPHER_PHONEME_Z (29)
-//
-//#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0)
-//#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1)
-//#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2)
-//
-//#define AL_VOCAL_MORPHER_MIN_WAVEFORM (0)
-//#define AL_VOCAL_MORPHER_MAX_WAVEFORM (2)
-//#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0)
-//
-//#define AL_VOCAL_MORPHER_MIN_RATE (0.0f)
-//#define AL_VOCAL_MORPHER_MAX_RATE (10.0f)
-//#define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f)
-//
-///* Pitch shifter effect */
-//#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12)
-//#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12)
-//#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12)
-//
-//#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50)
-//#define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50)
-//#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0)
-//
-///* Ring modulator effect */
-//#define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f)
-//#define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f)
-//#define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f)
-//
-//#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f)
-//#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f)
-//#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f)
-//
-//#define AL_RING_MODULATOR_SINUSOID (0)
-//#define AL_RING_MODULATOR_SAWTOOTH (1)
-//#define AL_RING_MODULATOR_SQUARE (2)
-//
-//#define AL_RING_MODULATOR_MIN_WAVEFORM (0)
-//#define AL_RING_MODULATOR_MAX_WAVEFORM (2)
-//#define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0)
-//
-///* Autowah effect */
-//#define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f)
-//#define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f)
-//#define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f)
-//
-//#define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f)
-//#define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f)
-//#define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f)
-//
-//#define AL_AUTOWAH_MIN_RESONANCE (2.0f)
-//#define AL_AUTOWAH_MAX_RESONANCE (1000.0f)
-//#define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f)
-//
-//#define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f)
-//#define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f)
-//#define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f)
-//
-///* Compressor effect */
-//#define AL_COMPRESSOR_MIN_ONOFF (0)
-//#define AL_COMPRESSOR_MAX_ONOFF (1)
-//#define AL_COMPRESSOR_DEFAULT_ONOFF (1)
-//
-///* Equalizer effect */
-//#define AL_EQUALIZER_MIN_LOW_GAIN (0.126f)
-//#define AL_EQUALIZER_MAX_LOW_GAIN (7.943f)
-//#define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f)
-//
-//#define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f)
-//#define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f)
-//#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f)
-//
-//#define AL_EQUALIZER_MIN_MID1_GAIN (0.126f)
-//#define AL_EQUALIZER_MAX_MID1_GAIN (7.943f)
-//#define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f)
-//
-//#define AL_EQUALIZER_MIN_MID1_CENTER (200.0f)
-//#define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f)
-//#define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f)
-//
-//#define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f)
-//#define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f)
-//#define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f)
-//
-//#define AL_EQUALIZER_MIN_MID2_GAIN (0.126f)
-//#define AL_EQUALIZER_MAX_MID2_GAIN (7.943f)
-//#define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f)
-//
-//#define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f)
-//#define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f)
-//#define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f)
-//
-//#define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f)
-//#define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f)
-//#define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f)
-//
-//#define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f)
-//#define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f)
-//#define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f)
-//
-//#define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f)
-//#define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f)
-//#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f)
-//
-//
-///* Source parameter value ranges and defaults. */
-//#define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f)
-//#define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f)
-//#define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f)
-//
-//#define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f)
-//#define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f)
-//#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f)
-//
-//#define AL_MIN_CONE_OUTER_GAINHF (0.0f)
-//#define AL_MAX_CONE_OUTER_GAINHF (1.0f)
-//#define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f)
-//
-//#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE
-//#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE
-//#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE
-//
-//#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE
-//#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
-//#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE
-//
-//#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE
-//#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
-//#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE
-//
-//
-///* Listener parameter value ranges and defaults. */
-//#define AL_MIN_METERS_PER_UNIT FLT_MIN
-//#define AL_MAX_METERS_PER_UNIT FLT_MAX
-//#define AL_DEFAULT_METERS_PER_UNIT (1.0f)
-
-
- public static String GetALErrorMsg(int errorCode) {
- String errorText;
- switch (errorCode) {
- case AL_NO_ERROR:
- errorText = "No Error";
- break;
- case AL_INVALID_NAME:
- errorText = "Invalid Name";
- break;
- case AL_INVALID_ENUM:
- errorText = "Invalid Enum";
- break;
- case AL_INVALID_VALUE:
- errorText = "Invalid Value";
- break;
- case AL_INVALID_OPERATION:
- errorText = "Invalid Operation";
- break;
- case AL_OUT_OF_MEMORY:
- errorText = "Out of Memory";
- break;
- default:
- errorText = "Unknown Error Code: " + String.valueOf(errorCode);
- }
- return errorText;
- }
-}
-
diff --git a/jme3-ios/src/main/java/com/jme3/audio/android/AndroidAudioData.java b/jme3-ios/src/main/java/com/jme3/audio/android/AndroidAudioData.java
deleted file mode 100644
index e7f4a0f98..000000000
--- a/jme3-ios/src/main/java/com/jme3/audio/android/AndroidAudioData.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.jme3.audio.android;
-
-import com.jme3.asset.AssetKey;
-import com.jme3.audio.AudioData;
-import com.jme3.audio.AudioRenderer;
-import com.jme3.util.NativeObject;
-
-public class AndroidAudioData extends AudioData {
-
- protected AssetKey> assetKey;
- protected float currentVolume = 0f;
-
- public AndroidAudioData(){
- super();
- }
-
- protected AndroidAudioData(int id){
- super(id);
- }
-
- public AssetKey> getAssetKey() {
- return assetKey;
- }
-
- public void setAssetKey(AssetKey> assetKey) {
- this.assetKey = assetKey;
- }
-
- @Override
- public DataType getDataType() {
- return DataType.Buffer;
- }
-
- @Override
- public float getDuration() {
- return 0; // TODO: ???
- }
-
- @Override
- public void resetObject() {
- this.id = -1;
- setUpdateNeeded();
- }
-
- @Override
- public void deleteObject(Object rendererObject) {
- ((AudioRenderer)rendererObject).deleteAudioData(this);
- }
-
- public float getCurrentVolume() {
- return currentVolume;
- }
-
- public void setCurrentVolume(float currentVolume) {
- this.currentVolume = currentVolume;
- }
-
- @Override
- public NativeObject createDestructableClone() {
- return new AndroidAudioData(id);
- }
-
- @Override
- public long getUniqueId() {
- return ((long)OBJTYPE_AUDIOBUFFER << 32) | ((long)id);
- }
-}
diff --git a/jme3-ios/src/main/java/com/jme3/audio/ios/IosAL.java b/jme3-ios/src/main/java/com/jme3/audio/ios/IosAL.java
new file mode 100644
index 000000000..7812dc748
--- /dev/null
+++ b/jme3-ios/src/main/java/com/jme3/audio/ios/IosAL.java
@@ -0,0 +1,53 @@
+package com.jme3.audio.ios;
+
+import com.jme3.audio.openal.AL;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+public final class IosAL implements AL {
+
+ public IosAL() {
+ }
+
+ public native String alGetString(int parameter);
+
+ public native int alGenSources();
+
+ public native int alGetError();
+
+ public native void alDeleteSources(int numSources, IntBuffer sources);
+
+ public native void alGenBuffers(int numBuffers, IntBuffer buffers);
+
+ public native void alDeleteBuffers(int numBuffers, IntBuffer buffers);
+
+ public native void alSourceStop(int source);
+
+ public native void alSourcei(int source, int param, int value);
+
+ public native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency);
+
+ public native void alSourcePlay(int source);
+
+ public native void alSourcePause(int source);
+
+ public native void alSourcef(int source, int param, float value);
+
+ public native void alSource3f(int source, int param, float value1, float value2, float value3);
+
+ public native int alGetSourcei(int source, int param);
+
+ public native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers);
+
+ public native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers);
+
+ public native void alListener(int param, FloatBuffer data);
+
+ public native void alListenerf(int param, float value);
+
+ public native void alListener3f(int param, float value1, float value2, float value3);
+
+ public native void alSource3i(int source, int param, int value1, int value2, int value3);
+
+}
diff --git a/jme3-ios/src/main/java/com/jme3/audio/ios/IosALC.java b/jme3-ios/src/main/java/com/jme3/audio/ios/IosALC.java
new file mode 100644
index 000000000..f1579c9e5
--- /dev/null
+++ b/jme3-ios/src/main/java/com/jme3/audio/ios/IosALC.java
@@ -0,0 +1,26 @@
+package com.jme3.audio.ios;
+
+import com.jme3.audio.openal.ALC;
+import java.nio.IntBuffer;
+
+public final class IosALC implements ALC {
+
+ public IosALC() {
+ }
+
+ public native void createALC();
+
+ public native void destroyALC();
+
+ public native boolean isCreated();
+
+ public native String alcGetString(int parameter);
+
+ public native boolean alcIsExtensionPresent(String extension);
+
+ public native void alcGetInteger(int param, IntBuffer buffer, int size);
+
+ public native void alcDevicePauseSOFT();
+
+ public native void alcDeviceResumeSOFT();
+}
diff --git a/jme3-ios/src/main/java/com/jme3/audio/ios/IosEFX.java b/jme3-ios/src/main/java/com/jme3/audio/ios/IosEFX.java
new file mode 100644
index 000000000..d7a569c1f
--- /dev/null
+++ b/jme3-ios/src/main/java/com/jme3/audio/ios/IosEFX.java
@@ -0,0 +1,32 @@
+package com.jme3.audio.ios;
+
+import com.jme3.audio.openal.EFX;
+import java.nio.IntBuffer;
+
+public class IosEFX implements EFX {
+
+ public IosEFX() {
+ }
+
+ public native void alGenAuxiliaryEffectSlots(int numSlots, IntBuffer buffers);
+
+ public native void alGenEffects(int numEffects, IntBuffer buffers);
+
+ public native void alEffecti(int effect, int param, int value);
+
+ public native void alAuxiliaryEffectSloti(int effectSlot, int param, int value);
+
+ public native void alDeleteEffects(int numEffects, IntBuffer buffers);
+
+ public native void alDeleteAuxiliaryEffectSlots(int numEffectSlots, IntBuffer buffers);
+
+ public native void alGenFilters(int numFilters, IntBuffer buffers);
+
+ public native void alFilteri(int filter, int param, int value);
+
+ public native void alFilterf(int filter, int param, float value);
+
+ public native void alDeleteFilters(int numFilters, IntBuffer buffers);
+
+ public native void alEffectf(int effect, int param, float value);
+}
diff --git a/jme3-ios/src/main/java/com/jme3/audio/plugins/AndroidAudioLoader.java b/jme3-ios/src/main/java/com/jme3/audio/plugins/AndroidAudioLoader.java
deleted file mode 100644
index a11425b23..000000000
--- a/jme3-ios/src/main/java/com/jme3/audio/plugins/AndroidAudioLoader.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.jme3.audio.plugins;
-
-import com.jme3.asset.AssetInfo;
-import com.jme3.asset.AssetLoader;
-import com.jme3.audio.android.AndroidAudioData;
-import java.io.IOException;
-
-/**
- * AndroidAudioLoader will create an
- * {@link AndroidAudioData} object with the specified asset key.
- */
-public class AndroidAudioLoader implements AssetLoader {
-
- @Override
- public Object load(AssetInfo assetInfo) throws IOException {
- AndroidAudioData result = new AndroidAudioData();
- result.setAssetKey(assetInfo.getKey());
- return result;
- }
-}
diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java
deleted file mode 100644
index 8a59d0be8..000000000
--- a/jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java
+++ /dev/null
@@ -1,2573 +0,0 @@
-package com.jme3.renderer.ios;
-
-import com.jme3.light.LightList;
-import com.jme3.material.RenderState;
-import com.jme3.math.*;
-import com.jme3.renderer.*;
-import com.jme3.scene.Mesh;
-import com.jme3.scene.Mesh.Mode;
-import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.VertexBuffer.Format;
-import com.jme3.scene.VertexBuffer.Type;
-import com.jme3.scene.VertexBuffer.Usage;
-import com.jme3.shader.Attribute;
-import com.jme3.shader.Shader;
-import com.jme3.shader.Shader.ShaderSource;
-import com.jme3.shader.Shader.ShaderType;
-import com.jme3.shader.Uniform;
-import com.jme3.texture.FrameBuffer;
-import com.jme3.texture.FrameBuffer.RenderBuffer;
-import com.jme3.texture.Image;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture.WrapAxis;
-import com.jme3.util.BufferUtils;
-import com.jme3.util.ListMap;
-import com.jme3.util.NativeObjectManager;
-
-import java.nio.*;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import jme3tools.shader.ShaderDebug;
-
-/**
- * The Renderer is responsible for taking rendering commands and
- * executing them on the underlying video hardware.
- *
- * @author Kirill Vainer
- */
-public class IGLESShaderRenderer implements Renderer {
-
- private static final Logger logger = Logger.getLogger(IGLESShaderRenderer.class.getName());
- private static final boolean VALIDATE_SHADER = false;
-
- private final NativeObjectManager objManager = new NativeObjectManager();
- private final EnumSet caps = EnumSet.noneOf(Caps.class);
- private final Statistics statistics = new Statistics();
- private final StringBuilder stringBuf = new StringBuilder(250);
- private final RenderContext context = new RenderContext();
- private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);
-
- private final int maxFBOAttachs = 1; // Only 1 color attachment on ES
- private final int maxMRTFBOAttachs = 1; // FIXME for now, not sure if > 1 is needed for ES
-
- private final int[] intBuf1 = new int[1];
- private final int[] intBuf16 = new int[16];
-
- private int glslVer;
- private int vertexTextureUnits;
- private int fragTextureUnits;
- private int vertexUniforms;
- private int fragUniforms;
- private int vertexAttribs;
- private int maxRBSize;
- private int maxTexSize;
- private int maxCubeTexSize;
-
- private FrameBuffer lastFb = null;
- private FrameBuffer mainFbOverride = null;
- private boolean useVBO = true;
- private boolean powerVr = false;
- private boolean uintIndexSupport = false;
-
- private Shader boundShader;
-
- private int vpX, vpY, vpW, vpH;
- private int clipX, clipY, clipW, clipH;
-
- public IGLESShaderRenderer() {
- logger.log(Level.FINE, "IGLESShaderRenderer Constructor");
- }
-
- /**
- * Get the capabilities of the renderer.
- * @return The capabilities of the renderer.
- */
- public EnumSet getCaps() {
- logger.log(Level.FINE, "IGLESShaderRenderer getCaps");
- return caps;
- }
-
- /**
- * The statistics allow tracking of how data
- * per frame, such as number of objects rendered, number of triangles, etc.
- * These are updated when the Renderer's methods are used, make sure
- * to call {@link Statistics#clearFrame() } at the appropriate time
- * to get accurate info per frame.
- */
- public Statistics getStatistics() {
- logger.log(Level.FINE, "IGLESShaderRenderer getStatistics");
- return statistics;
- }
-
- /**
- * Invalidates the current rendering state. Should be called after
- * the GL state was changed manually or through an external library.
- */
- public void invalidateState() {
- logger.log(Level.FINE, "IGLESShaderRenderer invalidateState");
- }
-
- /**
- * Clears certain channels of the currently bound framebuffer.
- *
- * @param color True if to clear colors (RGBA)
- * @param depth True if to clear depth/z
- * @param stencil True if to clear stencil buffer (if available, otherwise
- * ignored)
- */
- public void clearBuffers(boolean color, boolean depth, boolean stencil) {
- logger.log(Level.FINE, "IGLESShaderRenderer clearBuffers");
- int bits = 0;
- if (color) {
- //See explanations of the depth below, we must enable color write to be able to clear the color buffer
- if (context.colorWriteEnabled == false) {
- JmeIosGLES.glColorMask(true, true, true, true);
- context.colorWriteEnabled = true;
- }
- bits = JmeIosGLES.GL_COLOR_BUFFER_BIT;
- }
- if (depth) {
- //glClear(GL_DEPTH_BUFFER_BIT) seems to not work when glDepthMask is false
- //here s some link on openl board
- //http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=257223
- //if depth clear is requested, we enable the depthMask
- if (context.depthWriteEnabled == false) {
- JmeIosGLES.glDepthMask(true);
- context.depthWriteEnabled = true;
- }
- bits |= JmeIosGLES.GL_DEPTH_BUFFER_BIT;
- }
- if (stencil) {
- bits |= JmeIosGLES.GL_STENCIL_BUFFER_BIT;
- }
- if (bits != 0) {
- JmeIosGLES.glClear(bits);
- JmeIosGLES.checkGLError();
- }
- }
-
- /**
- * Sets the background (aka clear) color.
- *
- * @param color The background color to set
- */
- public void setBackgroundColor(ColorRGBA color) {
- logger.log(Level.FINE, "IGLESShaderRenderer setBackgroundColor");
- JmeIosGLES.glClearColor(color.r, color.g, color.b, color.a);
- JmeIosGLES.checkGLError();
- }
-
- /**
- * Applies the given {@link RenderState}, making the necessary
- * GL calls so that the state is applied.
- */
- public void applyRenderState(RenderState state) {
- logger.log(Level.FINE, "IGLESShaderRenderer applyRenderState");
- /*
- if (state.isWireframe() && !context.wireframe){
- GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_LINE);
- context.wireframe = true;
- }else if (!state.isWireframe() && context.wireframe){
- GLES20.glPolygonMode(GLES20.GL_FRONT_AND_BACK, GLES20.GL_FILL);
- context.wireframe = false;
- }
- */
- if (state.isDepthTest() && !context.depthTestEnabled) {
- JmeIosGLES.glEnable(JmeIosGLES.GL_DEPTH_TEST);
- JmeIosGLES.glDepthFunc(convertTestFunction(context.depthFunc));
- JmeIosGLES.checkGLError();
- context.depthTestEnabled = true;
- } else if (!state.isDepthTest() && context.depthTestEnabled) {
- JmeIosGLES.glDisable(JmeIosGLES.GL_DEPTH_TEST);
- JmeIosGLES.checkGLError();
- context.depthTestEnabled = false;
- }
- if (state.getDepthFunc() != context.depthFunc) {
- JmeIosGLES.glDepthFunc(convertTestFunction(state.getDepthFunc()));
- context.depthFunc = state.getDepthFunc();
- }
-
- if (state.isDepthWrite() && !context.depthWriteEnabled) {
- JmeIosGLES.glDepthMask(true);
- JmeIosGLES.checkGLError();
- context.depthWriteEnabled = true;
- } else if (!state.isDepthWrite() && context.depthWriteEnabled) {
- JmeIosGLES.glDepthMask(false);
- JmeIosGLES.checkGLError();
- context.depthWriteEnabled = false;
- }
- if (state.isColorWrite() && !context.colorWriteEnabled) {
- JmeIosGLES.glColorMask(true, true, true, true);
- JmeIosGLES.checkGLError();
- context.colorWriteEnabled = true;
- } else if (!state.isColorWrite() && context.colorWriteEnabled) {
- JmeIosGLES.glColorMask(false, false, false, false);
- JmeIosGLES.checkGLError();
- context.colorWriteEnabled = false;
- }
-// if (state.isPointSprite() && !context.pointSprite) {
-//// GLES20.glEnable(GLES20.GL_POINT_SPRITE);
-//// GLES20.glTexEnvi(GLES20.GL_POINT_SPRITE, GLES20.GL_COORD_REPLACE, GLES20.GL_TRUE);
-//// GLES20.glEnable(GLES20.GL_VERTEX_PROGRAM_POINT_SIZE);
-//// GLES20.glPointParameterf(GLES20.GL_POINT_SIZE_MIN, 1.0f);
-// } else if (!state.isPointSprite() && context.pointSprite) {
-//// GLES20.glDisable(GLES20.GL_POINT_SPRITE);
-// }
-
- if (state.isPolyOffset()) {
- if (!context.polyOffsetEnabled) {
- JmeIosGLES.glEnable(JmeIosGLES.GL_POLYGON_OFFSET_FILL);
- JmeIosGLES.glPolygonOffset(state.getPolyOffsetFactor(),
- state.getPolyOffsetUnits());
- JmeIosGLES.checkGLError();
-
- context.polyOffsetEnabled = true;
- context.polyOffsetFactor = state.getPolyOffsetFactor();
- context.polyOffsetUnits = state.getPolyOffsetUnits();
- } else {
- if (state.getPolyOffsetFactor() != context.polyOffsetFactor
- || state.getPolyOffsetUnits() != context.polyOffsetUnits) {
- JmeIosGLES.glPolygonOffset(state.getPolyOffsetFactor(),
- state.getPolyOffsetUnits());
- JmeIosGLES.checkGLError();
-
- context.polyOffsetFactor = state.getPolyOffsetFactor();
- context.polyOffsetUnits = state.getPolyOffsetUnits();
- }
- }
- } else {
- if (context.polyOffsetEnabled) {
- JmeIosGLES.glDisable(JmeIosGLES.GL_POLYGON_OFFSET_FILL);
- JmeIosGLES.checkGLError();
-
- context.polyOffsetEnabled = false;
- context.polyOffsetFactor = 0;
- context.polyOffsetUnits = 0;
- }
- }
- if (state.getFaceCullMode() != context.cullMode) {
- if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) {
- JmeIosGLES.glDisable(JmeIosGLES.GL_CULL_FACE);
- JmeIosGLES.checkGLError();
- } else {
- JmeIosGLES.glEnable(JmeIosGLES.GL_CULL_FACE);
- JmeIosGLES.checkGLError();
- }
-
- switch (state.getFaceCullMode()) {
- case Off:
- break;
- case Back:
- JmeIosGLES.glCullFace(JmeIosGLES.GL_BACK);
- JmeIosGLES.checkGLError();
- break;
- case Front:
- JmeIosGLES.glCullFace(JmeIosGLES.GL_FRONT);
- JmeIosGLES.checkGLError();
- break;
- case FrontAndBack:
- JmeIosGLES.glCullFace(JmeIosGLES.GL_FRONT_AND_BACK);
- JmeIosGLES.checkGLError();
- break;
- default:
- throw new UnsupportedOperationException("Unrecognized face cull mode: "
- + state.getFaceCullMode());
- }
-
- context.cullMode = state.getFaceCullMode();
- }
-
- if (state.getBlendMode() != context.blendMode) {
- if (state.getBlendMode() == RenderState.BlendMode.Off) {
- JmeIosGLES.glDisable(JmeIosGLES.GL_BLEND);
- JmeIosGLES.checkGLError();
- } else {
- JmeIosGLES.glEnable(JmeIosGLES.GL_BLEND);
- switch (state.getBlendMode()) {
- case Off:
- break;
- case Additive:
- JmeIosGLES.glBlendFunc(JmeIosGLES.GL_ONE, JmeIosGLES.GL_ONE);
- break;
- case AlphaAdditive:
- JmeIosGLES.glBlendFunc(JmeIosGLES.GL_SRC_ALPHA, JmeIosGLES.GL_ONE);
- break;
- case Color:
- JmeIosGLES.glBlendFunc(JmeIosGLES.GL_ONE, JmeIosGLES.GL_ONE_MINUS_SRC_COLOR);
- break;
- case Alpha:
- JmeIosGLES.glBlendFunc(JmeIosGLES.GL_SRC_ALPHA, JmeIosGLES.GL_ONE_MINUS_SRC_ALPHA);
- break;
- case PremultAlpha:
- JmeIosGLES.glBlendFunc(JmeIosGLES.GL_ONE, JmeIosGLES.GL_ONE_MINUS_SRC_ALPHA);
- break;
- case Modulate:
- JmeIosGLES.glBlendFunc(JmeIosGLES.GL_DST_COLOR, JmeIosGLES.GL_ZERO);
- break;
- case ModulateX2:
- JmeIosGLES.glBlendFunc(JmeIosGLES.GL_DST_COLOR, JmeIosGLES.GL_SRC_COLOR);
- break;
- default:
- throw new UnsupportedOperationException("Unrecognized blend mode: "
- + state.getBlendMode());
- }
- JmeIosGLES.checkGLError();
- }
- context.blendMode = state.getBlendMode();
- }
- }
-
- /**
- * Set the range of the depth values for objects. All rendered
- * objects will have their depth clamped to this range.
- *
- * @param start The range start
- * @param end The range end
- */
- public void setDepthRange(float start, float end) {
- logger.log(Level.FINE, "IGLESShaderRenderer setDepthRange");
- JmeIosGLES.glDepthRangef(start, end);
- JmeIosGLES.checkGLError();
- }
-
- /**
- * Called when a new frame has been rendered.
- */
- public void postFrame() {
- logger.log(Level.FINE, "IGLESShaderRenderer onFrame");
- //JmeIosGLES.checkGLErrorForced();
- JmeIosGLES.checkGLError();
-
- objManager.deleteUnused(this);
- }
-
- /**
- * Set the world matrix to use. Does nothing if the Renderer is
- * shader based.
- *
- * @param worldMatrix World matrix to use.
- */
- public void setWorldMatrix(Matrix4f worldMatrix) {
- logger.log(Level.FINE, "IGLESShaderRenderer setWorldMatrix");
- }
-
- /**
- * Sets the view and projection matrices to use. Does nothing if the Renderer
- * is shader based.
- *
- * @param viewMatrix The view matrix to use.
- * @param projMatrix The projection matrix to use.
- */
- public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix) {
- logger.log(Level.FINE, "IGLESShaderRenderer setViewProjectionMatrices");
- }
-
- /**
- * Set the viewport location and resolution on the screen.
- *
- * @param x The x coordinate of the viewport
- * @param y The y coordinate of the viewport
- * @param width Width of the viewport
- * @param height Height of the viewport
- */
- public void setViewPort(int x, int y, int width, int height) {
- logger.log(Level.FINE, "IGLESShaderRenderer setViewPort");
- if (x != vpX || vpY != y || vpW != width || vpH != height) {
- JmeIosGLES.glViewport(x, y, width, height);
- JmeIosGLES.checkGLError();
-
- vpX = x;
- vpY = y;
- vpW = width;
- vpH = height;
- }
- }
-
- /**
- * Specifies a clipping rectangle.
- * For all future rendering commands, no pixels will be allowed
- * to be rendered outside of the clip rectangle.
- *
- * @param x The x coordinate of the clip rect
- * @param y The y coordinate of the clip rect
- * @param width Width of the clip rect
- * @param height Height of the clip rect
- */
- public void setClipRect(int x, int y, int width, int height) {
- logger.log(Level.FINE, "IGLESShaderRenderer setClipRect");
- if (!context.clipRectEnabled) {
- JmeIosGLES.glEnable(JmeIosGLES.GL_SCISSOR_TEST);
- JmeIosGLES.checkGLError();
- context.clipRectEnabled = true;
- }
- if (clipX != x || clipY != y || clipW != width || clipH != height) {
- JmeIosGLES.glScissor(x, y, width, height);
- JmeIosGLES.checkGLError();
- clipX = x;
- clipY = y;
- clipW = width;
- clipH = height;
- }
- }
-
- /**
- * Clears the clipping rectangle set with
- * {@link #setClipRect(int, int, int, int) }.
- */
- public void clearClipRect() {
- logger.log(Level.FINE, "IGLESShaderRenderer clearClipRect");
- if (context.clipRectEnabled) {
- JmeIosGLES.glDisable(JmeIosGLES.GL_SCISSOR_TEST);
- JmeIosGLES.checkGLError();
- context.clipRectEnabled = false;
-
- clipX = 0;
- clipY = 0;
- clipW = 0;
- clipH = 0;
- }
- }
-
- /**
- * Set lighting state.
- * Does nothing if the renderer is shader based.
- * The lights should be provided in world space.
- * Specify null to disable lighting.
- *
- * @param lights The light list to set.
- */
- public void setLighting(LightList lights) {
- logger.log(Level.FINE, "IGLESShaderRenderer setLighting");
- }
-
- /**
- * Sets the shader to use for rendering.
- * If the shader has not been uploaded yet, it is compiled
- * and linked. If it has been uploaded, then the
- * uniform data is updated and the shader is set.
- *
- * @param shader The shader to use for rendering.
- */
- public void setShader(Shader shader) {
- logger.log(Level.FINE, "IGLESShaderRenderer setShader");
- if (shader == null) {
- throw new IllegalArgumentException("Shader cannot be null");
- } else {
- if (shader.isUpdateNeeded()) {
- updateShaderData(shader);
- }
-
- // NOTE: might want to check if any of the
- // sources need an update?
-
- assert shader.getId() > 0;
-
- updateShaderUniforms(shader);
- bindProgram(shader);
- }
- }
-
- /**
- * Deletes a shader. This method also deletes
- * the attached shader sources.
- *
- * @param shader Shader to delete.
- */
- public void deleteShader(Shader shader) {
- logger.log(Level.FINE, "IGLESShaderRenderer deleteShader");
- if (shader.getId() == -1) {
- logger.warning("Shader is not uploaded to GPU, cannot delete.");
- return;
- }
-
- for (ShaderSource source : shader.getSources()) {
- if (source.getId() != -1) {
- JmeIosGLES.glDetachShader(shader.getId(), source.getId());
- JmeIosGLES.checkGLError();
-
- deleteShaderSource(source);
- }
- }
-
- JmeIosGLES.glDeleteProgram(shader.getId());
- JmeIosGLES.checkGLError();
-
- statistics.onDeleteShader();
- shader.resetObject();
- }
-
- /**
- * Deletes the provided shader source.
- *
- * @param source The ShaderSource to delete.
- */
- public void deleteShaderSource(ShaderSource source) {
- logger.log(Level.FINE, "IGLESShaderRenderer deleteShaderSource");
- if (source.getId() < 0) {
- logger.warning("Shader source is not uploaded to GPU, cannot delete.");
- return;
- }
-
- source.clearUpdateNeeded();
-
- JmeIosGLES.glDeleteShader(source.getId());
- JmeIosGLES.checkGLError();
-
- source.resetObject();
- }
-
- /**
- * Copies contents from src to dst, scaling if necessary.
- */
- public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) {
- logger.log(Level.FINE, "IGLESShaderRenderer copyFrameBuffer");
- copyFrameBuffer(src, dst, true);
- }
-
- /**
- * Copies contents from src to dst, scaling if necessary.
- * set copyDepth to false to only copy the color buffers.
- */
- public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) {
- logger.warning("IGLESShaderRenderer copyFrameBuffer with depth TODO");
- throw new RendererException("Copy framebuffer not implemented yet.");
- }
-
- /**
- * Sets the framebuffer that will be drawn to.
- */
- public void setFrameBuffer(FrameBuffer fb) {
- logger.log(Level.FINE, "IGLESShaderRenderer setFrameBuffer");
- if (fb == null && mainFbOverride != null) {
- fb = mainFbOverride;
- }
-
- if (lastFb == fb) {
- if (fb == null || !fb.isUpdateNeeded()) {
- return;
- }
- }
-
- // generate mipmaps for last FB if needed
- if (lastFb != null) {
- for (int i = 0; i < lastFb.getNumColorBuffers(); i++) {
- RenderBuffer rb = lastFb.getColorBuffer(i);
- Texture tex = rb.getTexture();
- if (tex != null
- && tex.getMinFilter().usesMipMapLevels()) {
- setTexture(0, rb.getTexture());
-
-// int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace());
- int textureType = convertTextureType(tex.getType());
- JmeIosGLES.glGenerateMipmap(textureType);
- JmeIosGLES.checkGLError();
- }
- }
- }
-
- if (fb == null) {
- // unbind any fbos
- if (context.boundFBO != 0) {
- JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, 0);
- JmeIosGLES.checkGLError();
-
- statistics.onFrameBufferUse(null, true);
-
- context.boundFBO = 0;
- }
-
- /*
- // select back buffer
- if (context.boundDrawBuf != -1) {
- glDrawBuffer(initialDrawBuf);
- context.boundDrawBuf = -1;
- }
- if (context.boundReadBuf != -1) {
- glReadBuffer(initialReadBuf);
- context.boundReadBuf = -1;
- }
- */
-
- lastFb = null;
- } else {
- if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) {
- throw new IllegalArgumentException("The framebuffer: " + fb
- + "\nDoesn't have any color/depth buffers");
- }
-
- if (fb.isUpdateNeeded()) {
- updateFrameBuffer(fb);
- }
-
- if (context.boundFBO != fb.getId()) {
- JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, fb.getId());
- JmeIosGLES.checkGLError();
-
- statistics.onFrameBufferUse(fb, true);
-
- // update viewport to reflect framebuffer's resolution
- setViewPort(0, 0, fb.getWidth(), fb.getHeight());
-
- context.boundFBO = fb.getId();
- } else {
- statistics.onFrameBufferUse(fb, false);
- }
- if (fb.getNumColorBuffers() == 0) {
-// // make sure to select NONE as draw buf
-// // no color buffer attached. select NONE
- if (context.boundDrawBuf != -2) {
-// glDrawBuffer(GL_NONE);
- context.boundDrawBuf = -2;
- }
- if (context.boundReadBuf != -2) {
-// glReadBuffer(GL_NONE);
- context.boundReadBuf = -2;
- }
- } else {
- if (fb.getNumColorBuffers() > maxFBOAttachs) {
- throw new RendererException("Framebuffer has more color "
- + "attachments than are supported"
- + " by the video hardware!");
- }
- if (fb.isMultiTarget()) {
- if (fb.getNumColorBuffers() > maxMRTFBOAttachs) {
- throw new RendererException("Framebuffer has more"
- + " multi targets than are supported"
- + " by the video hardware!");
- }
-
- if (context.boundDrawBuf != 100 + fb.getNumColorBuffers()) {
- //intBuf16.clear();
- for (int i = 0; i < 16; i++) {
- intBuf16[i] = i < fb.getNumColorBuffers() ? JmeIosGLES.GL_COLOR_ATTACHMENT0 + i : 0;
- }
-
- //intBuf16.flip();// TODO: flip
-// glDrawBuffers(intBuf16);
- context.boundDrawBuf = 100 + fb.getNumColorBuffers();
- }
- } else {
- RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex());
- // select this draw buffer
- if (context.boundDrawBuf != rb.getSlot()) {
- JmeIosGLES.glActiveTexture(convertAttachmentSlot(rb.getSlot()));
- JmeIosGLES.checkGLError();
-
- context.boundDrawBuf = rb.getSlot();
- }
- }
- }
-
- assert fb.getId() >= 0;
- assert context.boundFBO == fb.getId();
-
- lastFb = fb;
-
- checkFrameBufferStatus(fb);
- }
- }
-
- /**
- * Set the framebuffer that will be set instead of the main framebuffer
- * when a call to setFrameBuffer(null) is made.
- *
- * @param fb
- */
- public void setMainFrameBufferOverride(FrameBuffer fb) {
- logger.log(Level.FINE, "IGLESShaderRenderer setMainFrameBufferOverride");
- mainFbOverride = fb;
- }
-
- /**
- * Reads the pixels currently stored in the specified framebuffer
- * into the given ByteBuffer object.
- * Only color pixels are transferred, the format is BGRA with 8 bits
- * per component. The given byte buffer should have at least
- * fb.getWidth() * fb.getHeight() * 4 bytes remaining.
- *
- * @param fb The framebuffer to read from
- * @param byteBuf The bytebuffer to transfer color data to
- */
- public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
- logger.log(Level.FINE, "IGLESShaderRenderer readFrameBuffer");
- if (fb != null) {
- RenderBuffer rb = fb.getColorBuffer();
- if (rb == null) {
- throw new IllegalArgumentException("Specified framebuffer"
- + " does not have a colorbuffer");
- }
-
- setFrameBuffer(fb);
- } else {
- setFrameBuffer(null);
- }
-
- //JmeIosGLES.glReadPixels2(vpX, vpY, vpW, vpH, JmeIosGLES.GL_RGBA, JmeIosGLES.GL_UNSIGNED_BYTE, byteBuf.array(), 0, vpW * vpH * 4);
- JmeIosGLES.glReadPixels(vpX, vpY, vpW, vpH, JmeIosGLES.GL_RGBA, JmeIosGLES.GL_UNSIGNED_BYTE, byteBuf);
- JmeIosGLES.checkGLError();
- }
-
- /**
- * Deletes a framebuffer and all attached renderbuffers
- */
- public void deleteFrameBuffer(FrameBuffer fb) {
- logger.log(Level.FINE, "IGLESShaderRenderer deleteFrameBuffer");
- if (fb.getId() != -1) {
- if (context.boundFBO == fb.getId()) {
- JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, 0);
- JmeIosGLES.checkGLError();
-
- context.boundFBO = 0;
- }
-
- if (fb.getDepthBuffer() != null) {
- deleteRenderBuffer(fb, fb.getDepthBuffer());
- }
- if (fb.getColorBuffer() != null) {
- deleteRenderBuffer(fb, fb.getColorBuffer());
- }
-
- intBuf1[0] = fb.getId();
- JmeIosGLES.glDeleteFramebuffers(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- fb.resetObject();
-
- statistics.onDeleteFrameBuffer();
- }
- }
-
- /**
- * Sets the texture to use for the given texture unit.
- */
- public void setTexture(int unit, Texture tex) {
- logger.log(Level.FINE, "IGLESShaderRenderer setTexture");
- Image image = tex.getImage();
- if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated()) ) {
- updateTexImageData(image, tex.getType());
- }
-
- int texId = image.getId();
- assert texId != -1;
-
- if (texId == -1) {
- logger.warning("error: texture image has -1 id");
- }
-
- Image[] textures = context.boundTextures;
-
- int type = convertTextureType(tex.getType());
- if (textures[unit] != image) {
- if (context.boundTextureUnit != unit) {
- JmeIosGLES.glActiveTexture(JmeIosGLES.GL_TEXTURE0 + unit);
- context.boundTextureUnit = unit;
- }
-
- JmeIosGLES.glBindTexture(type, texId);
- JmeIosGLES.checkGLError();
-
- textures[unit] = image;
-
- statistics.onTextureUse(tex.getImage(), true);
- } else {
- statistics.onTextureUse(tex.getImage(), false);
- }
-
- setupTextureParams(tex);
- }
-
- /**
- * Modify the given Texture tex with the given Image. The image will be put at x and y into the texture.
- *
- * @param tex the Texture that will be modified
- * @param pixels the source Image data to copy data from
- * @param x the x position to put the image into the texture
- * @param y the y position to put the image into the texture
- */
- public void modifyTexture(Texture tex, Image pixels, int x, int y) {
- logger.log(Level.FINE, "IGLESShaderRenderer modifyTexture");
- setTexture(0, tex);
- TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType()), 0, x, y);
- }
-
- /**
- * Deletes a texture from the GPU.
- */
- public void deleteImage(Image image) {
- logger.log(Level.FINE, "IGLESShaderRenderer deleteImage");
- int texId = image.getId();
- if (texId != -1) {
- intBuf1[0] = texId;
-
- JmeIosGLES.glDeleteTextures(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- image.resetObject();
-
- statistics.onDeleteTexture();
- }
- }
-
- /**
- * Uploads a vertex buffer to the GPU.
- *
- * @param vb The vertex buffer to upload
- */
- public void updateBufferData(VertexBuffer vb) {
- logger.log(Level.FINE, "IGLESShaderRenderer updateBufferData");
- int bufId = vb.getId();
- boolean created = false;
- if (bufId == -1) {
- // create buffer
- JmeIosGLES.glGenBuffers(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- bufId = intBuf1[0];
- vb.setId(bufId);
- objManager.registerObject(vb);
-
- created = true;
- }
-
- // bind buffer
- int target;
- if (vb.getBufferType() == VertexBuffer.Type.Index) {
- target = JmeIosGLES.GL_ELEMENT_ARRAY_BUFFER;
- if (context.boundElementArrayVBO != bufId) {
- JmeIosGLES.glBindBuffer(target, bufId);
- JmeIosGLES.checkGLError();
-
- context.boundElementArrayVBO = bufId;
- }
- } else {
- target = JmeIosGLES.GL_ARRAY_BUFFER;
- if (context.boundArrayVBO != bufId) {
- JmeIosGLES.glBindBuffer(target, bufId);
- JmeIosGLES.checkGLError();
-
- context.boundArrayVBO = bufId;
- }
- }
-
- int usage = convertUsage(vb.getUsage());
- vb.getData().rewind();
-
- if (created || vb.hasDataSizeChanged()) {
- // upload data based on format
- int size = vb.getData().limit() * vb.getFormat().getComponentSize();
-
- switch (vb.getFormat()) {
- case Byte:
- case UnsignedByte:
- JmeIosGLES.glBufferData(target, size, (ByteBuffer) vb.getData(), usage);
- JmeIosGLES.checkGLError();
- break;
- case Short:
- case UnsignedShort:
- JmeIosGLES.glBufferData(target, size, (ShortBuffer) vb.getData(), usage);
- JmeIosGLES.checkGLError();
- break;
- case Int:
- case UnsignedInt:
- JmeIosGLES.glBufferData(target, size, (IntBuffer) vb.getData(), usage);
- JmeIosGLES.checkGLError();
- break;
- case Float:
- JmeIosGLES.glBufferData(target, size, (FloatBuffer) vb.getData(), usage);
- JmeIosGLES.checkGLError();
- break;
- default:
- throw new RuntimeException("Unknown buffer format.");
- }
- } else {
- int size = vb.getData().limit() * vb.getFormat().getComponentSize();
-
- switch (vb.getFormat()) {
- case Byte:
- case UnsignedByte:
- JmeIosGLES.glBufferSubData(target, 0, size, (ByteBuffer) vb.getData());
- JmeIosGLES.checkGLError();
- break;
- case Short:
- case UnsignedShort:
- JmeIosGLES.glBufferSubData(target, 0, size, (ShortBuffer) vb.getData());
- JmeIosGLES.checkGLError();
- break;
- case Int:
- case UnsignedInt:
- JmeIosGLES.glBufferSubData(target, 0, size, (IntBuffer) vb.getData());
- JmeIosGLES.checkGLError();
- break;
- case Float:
- JmeIosGLES.glBufferSubData(target, 0, size, (FloatBuffer) vb.getData());
- JmeIosGLES.checkGLError();
- break;
- default:
- throw new RuntimeException("Unknown buffer format.");
- }
- }
- vb.clearUpdateNeeded();
- }
-
- /**
- * Deletes a vertex buffer from the GPU.
- * @param vb The vertex buffer to delete
- */
- public void deleteBuffer(VertexBuffer vb) {
- logger.log(Level.FINE, "IGLESShaderRenderer deleteBuffer");
- int bufId = vb.getId();
- if (bufId != -1) {
- // delete buffer
- intBuf1[0] = bufId;
-
- JmeIosGLES.glDeleteBuffers(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- vb.resetObject();
- }
- }
-
- /**
- * Renders count meshes, with the geometry data supplied.
- * The shader which is currently set with setShader is
- * responsible for transforming the input verticies into clip space
- * and shading it based on the given vertex attributes.
- * The int variable gl_InstanceID can be used to access the current
- * instance of the mesh being rendered inside the vertex shader.
- *
- * @param mesh The mesh to render
- * @param lod The LOD level to use, see {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }.
- * @param count Number of mesh instances to render
- */
- public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
- logger.log(Level.FINE, "IGLESShaderRenderer renderMesh");
- if (mesh.getVertexCount() == 0) {
- return;
- }
- /*
- * NOTE: not supported in OpenGL ES 2.0.
- if (context.pointSize != mesh.getPointSize()) {
- GLES10.glPointSize(mesh.getPointSize());
- context.pointSize = mesh.getPointSize();
- }
- */
- if (context.lineWidth != mesh.getLineWidth()) {
- JmeIosGLES.glLineWidth(mesh.getLineWidth());
- JmeIosGLES.checkGLError();
- context.lineWidth = mesh.getLineWidth();
- }
-
- statistics.onMeshDrawn(mesh, lod);
-// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
-// renderMeshVertexArray(mesh, lod, count);
-// }else{
-
- if (useVBO) {
- renderMeshDefault(mesh, lod, count);
- } else {
- renderMeshVertexArray(mesh, lod, count);
- }
- }
-
- /**
- * Resets all previously used {@link NativeObject Native Objects} on this Renderer.
- * The state of the native objects is reset in such way, that using
- * them again will cause the renderer to reupload them.
- * Call this method when you know the GL context is going to shutdown.
- *
- * @see NativeObject#resetObject()
- */
- public void resetGLObjects() {
- logger.log(Level.FINE, "IGLESShaderRenderer resetGLObjects");
- objManager.resetObjects();
- statistics.clearMemory();
- boundShader = null;
- lastFb = null;
- context.reset();
- }
-
- /**
- * Deletes all previously used {@link NativeObject Native Objects} on this Renderer, and
- * then resets the native objects.
- *
- * @see #resetGLObjects()
- * @see NativeObject#deleteObject(java.lang.Object)
- */
- public void cleanup() {
- logger.log(Level.FINE, "IGLESShaderRenderer cleanup");
- objManager.deleteAllObjects(this);
- statistics.clearMemory();
- }
-
- /**
- * Sets the alpha to coverage state.
- *
- * When alpha coverage and multi-sampling is enabled,
- * each pixel will contain alpha coverage in all
- * of its subsamples, which is then combined when
- * other future alpha-blended objects are rendered.
- *
- *
- * Alpha-to-coverage is useful for rendering transparent objects
- * without having to worry about sorting them.
- *
- */
- public void setAlphaToCoverage(boolean value) {
- logger.log(Level.FINE, "IGLESShaderRenderer setAlphaToCoverage");
- if (value) {
- JmeIosGLES.glEnable(JmeIosGLES.GL_SAMPLE_ALPHA_TO_COVERAGE);
- JmeIosGLES.checkGLError();
- } else {
- JmeIosGLES.glDisable(JmeIosGLES.GL_SAMPLE_ALPHA_TO_COVERAGE);
- JmeIosGLES.checkGLError();
- }
- }
-
-
- /* ------------------------------------------------------------------------------ */
-
-
- public void initialize() {
- Level store = logger.getLevel();
- logger.setLevel(Level.FINE);
-
- logger.log(Level.FINE, "Vendor: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_VENDOR));
- logger.log(Level.FINE, "Renderer: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_RENDERER));
- logger.log(Level.FINE, "Version: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_VERSION));
- logger.log(Level.FINE, "Shading Language Version: {0}", JmeIosGLES.glGetString(JmeIosGLES.GL_SHADING_LANGUAGE_VERSION));
-
- /*
- // Fix issue in TestRenderToMemory when GL_FRONT is the main
- // buffer being used.
- initialDrawBuf = glGetInteger(GL_DRAW_BUFFER);
- initialReadBuf = glGetInteger(GL_READ_BUFFER);
-
- // XXX: This has to be GL_BACK for canvas on Mac
- // Since initialDrawBuf is GL_FRONT for pbuffer, gotta
- // change this value later on ...
-// initialDrawBuf = GL_BACK;
-// initialReadBuf = GL_BACK;
- */
-
- // Check OpenGL version
- int openGlVer = extractVersion("OpenGL ES ", JmeIosGLES.glGetString(JmeIosGLES.GL_VERSION));
- if (openGlVer == -1) {
- glslVer = -1;
- throw new UnsupportedOperationException("OpenGL ES 2.0+ is required for IGLESShaderRenderer!");
- }
-
- // Check shader language version
- glslVer = extractVersion("OpenGL ES GLSL ES ", JmeIosGLES.glGetString(JmeIosGLES.GL_SHADING_LANGUAGE_VERSION));
- switch (glslVer) {
- // TODO: When new versions of OpenGL ES shader language come out,
- // update this.
- default:
- caps.add(Caps.GLSL100);
- break;
- }
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16, 0);
- vertexTextureUnits = intBuf16[0];
- logger.log(Level.FINE, "VTF Units: {0}", vertexTextureUnits);
- if (vertexTextureUnits > 0) {
- caps.add(Caps.VertexTextureFetch);
- }
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16, 0);
- fragTextureUnits = intBuf16[0];
- logger.log(Level.FINE, "Texture Units: {0}", fragTextureUnits);
-
- // Multiply vector count by 4 to get float count.
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VERTEX_UNIFORM_VECTORS, intBuf16, 0);
- vertexUniforms = intBuf16[0] * 4;
- logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms);
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_FRAGMENT_UNIFORM_VECTORS, intBuf16, 0);
- fragUniforms = intBuf16[0] * 4;
- logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VARYING_VECTORS, intBuf16, 0);
- int varyingFloats = intBuf16[0] * 4;
- logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats);
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_VERTEX_ATTRIBS, intBuf16, 0);
- vertexAttribs = intBuf16[0];
- logger.log(Level.FINE, "Vertex Attributes: {0}", vertexAttribs);
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_SUBPIXEL_BITS, intBuf16, 0);
- int subpixelBits = intBuf16[0];
- logger.log(Level.FINE, "Subpixel Bits: {0}", subpixelBits);
-
-// GLES10.glGetIntegerv(GLES10.GL_MAX_ELEMENTS_VERTICES, intBuf16);
-// maxVertCount = intBuf16.get(0);
-// logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount);
-//
-// GLES10.glGetIntegerv(GLES10.GL_MAX_ELEMENTS_INDICES, intBuf16);
-// maxTriCount = intBuf16.get(0);
-// logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount);
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_TEXTURE_SIZE, intBuf16, 0);
- maxTexSize = intBuf16[0];
- logger.log(Level.FINE, "Maximum Texture Resolution: {0}", maxTexSize);
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16, 0);
- maxCubeTexSize = intBuf16[0];
- logger.log(Level.FINE, "Maximum CubeMap Resolution: {0}", maxCubeTexSize);
-
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_MAX_RENDERBUFFER_SIZE, intBuf16, 0);
- maxRBSize = intBuf16[0];
- logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
-
- /*
- if (ctxCaps.GL_ARB_color_buffer_float){
- // XXX: Require both 16 and 32 bit float support for FloatColorBuffer.
- if (ctxCaps.GL_ARB_half_float_pixel){
- caps.add(Caps.FloatColorBuffer);
- }
- }
-
- if (ctxCaps.GL_ARB_depth_buffer_float){
- caps.add(Caps.FloatDepthBuffer);
- }
-
- if (ctxCaps.GL_ARB_draw_instanced)
- caps.add(Caps.MeshInstancing);
-
- if (ctxCaps.GL_ARB_texture_buffer_object)
- caps.add(Caps.TextureBuffer);
-
- if (ctxCaps.GL_ARB_texture_float){
- if (ctxCaps.GL_ARB_half_float_pixel){
- caps.add(Caps.FloatTexture);
- }
- }
-
- if (ctxCaps.GL_EXT_packed_float){
- caps.add(Caps.PackedFloatColorBuffer);
- if (ctxCaps.GL_ARB_half_float_pixel){
- // because textures are usually uploaded as RGB16F
- // need half-float pixel
- caps.add(Caps.PackedFloatTexture);
- }
- }
-
- if (ctxCaps.GL_EXT_texture_array)
- caps.add(Caps.TextureArray);
-
- if (ctxCaps.GL_EXT_texture_shared_exponent)
- caps.add(Caps.SharedExponentTexture);
-
- if (ctxCaps.GL_EXT_framebuffer_object){
- caps.add(Caps.FrameBuffer);
-
- glGetInteger(GL_MAX_RENDERBUFFER_SIZE_EXT, intBuf16);
- maxRBSize = intBuf16.get(0);
- logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
-
- glGetInteger(GL_MAX_COLOR_ATTACHMENTS_EXT, intBuf16);
- maxFBOAttachs = intBuf16.get(0);
- logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
-
- if (ctxCaps.GL_EXT_framebuffer_multisample){
- caps.add(Caps.FrameBufferMultisample);
-
- glGetInteger(GL_MAX_SAMPLES_EXT, intBuf16);
- maxFBOSamples = intBuf16.get(0);
- logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
- }
-
- if (ctxCaps.GL_ARB_draw_buffers){
- caps.add(Caps.FrameBufferMRT);
- glGetInteger(ARBDrawBuffers.GL_MAX_DRAW_BUFFERS_ARB, intBuf16);
- maxMRTFBOAttachs = intBuf16.get(0);
- logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs);
- }
- }
-
- if (ctxCaps.GL_ARB_multisample){
- glGetInteger(ARBMultisample.GL_SAMPLE_BUFFERS_ARB, intBuf16);
- boolean available = intBuf16.get(0) != 0;
- glGetInteger(ARBMultisample.GL_SAMPLES_ARB, intBuf16);
- int samples = intBuf16.get(0);
- logger.log(Level.FINER, "Samples: {0}", samples);
- boolean enabled = glIsEnabled(ARBMultisample.GL_MULTISAMPLE_ARB);
- if (samples > 0 && available && !enabled){
- glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
- }
- }
- */
-
- String extensions = JmeIosGLES.glGetString(JmeIosGLES.GL_EXTENSIONS);
- logger.log(Level.FINE, "GL_EXTENSIONS: {0}", extensions);
-
- // Get number of compressed formats available.
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_NUM_COMPRESSED_TEXTURE_FORMATS, intBuf16, 0);
- int numCompressedFormats = intBuf16[0];
-
- // Allocate buffer for compressed formats.
- int[] compressedFormats = new int[numCompressedFormats];
- JmeIosGLES.glGetIntegerv(JmeIosGLES.GL_COMPRESSED_TEXTURE_FORMATS, compressedFormats, 0);
-
- // Check for errors after all glGet calls.
- JmeIosGLES.checkGLError();
-
- // Print compressed formats.
- for (int i = 0; i < numCompressedFormats; i++) {
- logger.log(Level.FINE, "Compressed Texture Formats: {0}", compressedFormats[i]);
- }
-
- TextureUtil.loadTextureFeatures(extensions);
-
- applyRenderState(RenderState.DEFAULT);
- JmeIosGLES.glDisable(JmeIosGLES.GL_DITHER);
- JmeIosGLES.checkGLError();
-
- logger.log(Level.FINE, "Caps: {0}", caps);
- logger.setLevel(store);
-
- uintIndexSupport = extensions.contains("GL_OES_element_index_uint");
- logger.log(Level.FINE, "Support for UInt index: {0}", uintIndexSupport);
- }
-
-
- /* ------------------------------------------------------------------------------ */
-
-
- private int extractVersion(String prefixStr, String versionStr) {
- if (versionStr != null) {
- int spaceIdx = versionStr.indexOf(" ", prefixStr.length());
- if (spaceIdx >= 1) {
- versionStr = versionStr.substring(prefixStr.length(), spaceIdx).trim();
- } else {
- versionStr = versionStr.substring(prefixStr.length()).trim();
- }
- float version = Float.parseFloat(versionStr);
- return (int) (version * 100);
- } else {
- return -1;
- }
- }
-
- private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
- intBuf1[0] = rb.getId();
- JmeIosGLES.glDeleteRenderbuffers(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
- }
-
- private int convertUsage(Usage usage) {
- switch (usage) {
- case Static:
- return JmeIosGLES.GL_STATIC_DRAW;
- case Dynamic:
- return JmeIosGLES.GL_DYNAMIC_DRAW;
- case Stream:
- return JmeIosGLES.GL_STREAM_DRAW;
- default:
- throw new RuntimeException("Unknown usage type.");
- }
- }
-
-
- protected void bindProgram(Shader shader) {
- int shaderId = shader.getId();
- if (context.boundShaderProgram != shaderId) {
- JmeIosGLES.glUseProgram(shaderId);
- JmeIosGLES.checkGLError();
-
- statistics.onShaderUse(shader, true);
- boundShader = shader;
- context.boundShaderProgram = shaderId;
- } else {
- statistics.onShaderUse(shader, false);
- }
- }
-
- protected void updateShaderUniforms(Shader shader) {
- ListMap uniforms = shader.getUniformMap();
- for (int i = 0; i < uniforms.size(); i++) {
- Uniform uniform = uniforms.getValue(i);
- if (uniform.isUpdateNeeded()) {
- updateUniform(shader, uniform);
- }
- }
- }
-
-
- protected void updateUniform(Shader shader, Uniform uniform) {
- logger.log(Level.FINE, "IGLESShaderRenderer private updateUniform: " + uniform.getVarType());
- int shaderId = shader.getId();
-
- assert uniform.getName() != null;
- assert shader.getId() > 0;
-
- if (context.boundShaderProgram != shaderId) {
- JmeIosGLES.glUseProgram(shaderId);
- JmeIosGLES.checkGLError();
-
- statistics.onShaderUse(shader, true);
- boundShader = shader;
- context.boundShaderProgram = shaderId;
- } else {
- statistics.onShaderUse(shader, false);
- }
-
- int loc = uniform.getLocation();
- if (loc == -1) {
- return;
- }
-
- if (loc == -2) {
- // get uniform location
- updateUniformLocation(shader, uniform);
- if (uniform.getLocation() == -1) {
- // not declared, ignore
- uniform.clearUpdateNeeded();
- return;
- }
- loc = uniform.getLocation();
- }
-
- if (uniform.getVarType() == null) {
- // removed logging the warning to avoid flooding the log
- // (LWJGL also doesn't post a warning)
- //logger.log(Level.FINEST, "Uniform value is not set yet. Shader: {0}, Uniform: {1}",
- // new Object[]{shader.toString(), uniform.toString()});
- return; // value not set yet..
- }
-
- statistics.onUniformSet();
-
- uniform.clearUpdateNeeded();
- ByteBuffer bb;//GetPrimitiveArrayCritical
- FloatBuffer fb;
- IntBuffer ib;
- switch (uniform.getVarType()) {
- case Float:
- Float f = (Float) uniform.getValue();
- JmeIosGLES.glUniform1f(loc, f.floatValue());
- break;
- case Vector2:
- Vector2f v2 = (Vector2f) uniform.getValue();
- JmeIosGLES.glUniform2f(loc, v2.getX(), v2.getY());
- break;
- case Vector3:
- Vector3f v3 = (Vector3f) uniform.getValue();
- JmeIosGLES.glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ());
- break;
- case Vector4:
- Object val = uniform.getValue();
- if (val instanceof ColorRGBA) {
- ColorRGBA c = (ColorRGBA) val;
- JmeIosGLES.glUniform4f(loc, c.r, c.g, c.b, c.a);
- } else if (val instanceof Vector4f) {
- Vector4f c = (Vector4f) val;
- JmeIosGLES.glUniform4f(loc, c.x, c.y, c.z, c.w);
- } else {
- Quaternion c = (Quaternion) uniform.getValue();
- JmeIosGLES.glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW());
- }
- break;
- case Boolean:
- Boolean b = (Boolean) uniform.getValue();
- JmeIosGLES.glUniform1i(loc, b.booleanValue() ? JmeIosGLES.GL_TRUE : JmeIosGLES.GL_FALSE);
- break;
- case Matrix3:
- fb = (FloatBuffer) uniform.getValue();
- assert fb.remaining() == 9;
- JmeIosGLES.glUniformMatrix3fv(loc, 1, false, fb);
- break;
- case Matrix4:
- fb = (FloatBuffer) uniform.getValue();
- assert fb.remaining() == 16;
- JmeIosGLES.glUniformMatrix4fv(loc, 1, false, fb);
- break;
- case IntArray:
- ib = (IntBuffer) uniform.getValue();
- JmeIosGLES.glUniform1iv(loc, ib.limit(), ib);
- break;
- case FloatArray:
- fb = (FloatBuffer) uniform.getValue();
- JmeIosGLES.glUniform1fv(loc, fb.limit(), fb);
- break;
- case Vector2Array:
- fb = (FloatBuffer) uniform.getValue();
- JmeIosGLES.glUniform2fv(loc, fb.limit() / 2, fb);
- break;
- case Vector3Array:
- fb = (FloatBuffer) uniform.getValue();
- JmeIosGLES.glUniform3fv(loc, fb.limit() / 3, fb);
- break;
- case Vector4Array:
- fb = (FloatBuffer) uniform.getValue();
- JmeIosGLES.glUniform4fv(loc, fb.limit() / 4, fb);
- break;
- case Matrix4Array:
- fb = (FloatBuffer) uniform.getValue();
- JmeIosGLES.glUniformMatrix4fv(loc, fb.limit() / 16, false, fb);
- break;
- case Int:
- Integer i = (Integer) uniform.getValue();
- JmeIosGLES.glUniform1i(loc, i.intValue());
- break;
- default:
- throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
- }
- JmeIosGLES.checkGLError();
- }
-
- protected void updateUniformLocation(Shader shader, Uniform uniform) {
- stringBuf.setLength(0);
- stringBuf.append(uniform.getName()).append('\0');
- updateNameBuffer();
- int loc = JmeIosGLES.glGetUniformLocation(shader.getId(), uniform.getName());
- JmeIosGLES.checkGLError();
-
- if (loc < 0) {
- uniform.setLocation(-1);
- // uniform is not declared in shader
- } else {
- uniform.setLocation(loc);
- }
- }
-
- protected void updateNameBuffer() {
- int len = stringBuf.length();
-
- nameBuf.position(0);
- nameBuf.limit(len);
- for (int i = 0; i < len; i++) {
- nameBuf.put((byte) stringBuf.charAt(i));
- }
-
- nameBuf.rewind();
- }
-
-
- public void updateShaderData(Shader shader) {
- int id = shader.getId();
- boolean needRegister = false;
- if (id == -1) {
- // create program
- id = JmeIosGLES.glCreateProgram();
- JmeIosGLES.checkGLError();
-
- if (id <= 0) {
- throw new RendererException("Invalid ID received when trying to create shader program.");
- }
-
- shader.setId(id);
- needRegister = true;
- }
-
- for (ShaderSource source : shader.getSources()) {
- if (source.isUpdateNeeded()) {
- updateShaderSourceData(source);
- }
-
- JmeIosGLES.glAttachShader(id, source.getId());
- JmeIosGLES.checkGLError();
- }
-
- // link shaders to program
- JmeIosGLES.glLinkProgram(id);
- JmeIosGLES.checkGLError();
-
- JmeIosGLES.glGetProgramiv(id, JmeIosGLES.GL_LINK_STATUS, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- boolean linkOK = intBuf1[0] == JmeIosGLES.GL_TRUE;
- String infoLog = null;
-
- if (VALIDATE_SHADER || !linkOK) {
- JmeIosGLES.glGetProgramiv(id, JmeIosGLES.GL_INFO_LOG_LENGTH, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- int length = intBuf1[0];
- if (length > 3) {
- // get infos
- infoLog = JmeIosGLES.glGetProgramInfoLog(id);
- JmeIosGLES.checkGLError();
- }
- }
-
- if (linkOK) {
- if (infoLog != null) {
- logger.log(Level.FINE, "shader link success. \n{0}", infoLog);
- } else {
- logger.fine("shader link success");
- }
- shader.clearUpdateNeeded();
- if (needRegister) {
- // Register shader for clean up if it was created in this method.
- objManager.registerObject(shader);
- statistics.onNewShader();
- } else {
- // OpenGL spec: uniform locations may change after re-link
- resetUniformLocations(shader);
- }
- } else {
- if (infoLog != null) {
- throw new RendererException("Shader link failure, shader:" + shader + "\n" + infoLog);
- } else {
- throw new RendererException("Shader link failure, shader:" + shader + "\ninfo: ");
- }
- }
- }
-
- protected void resetUniformLocations(Shader shader) {
- ListMap uniforms = shader.getUniformMap();
- for (int i = 0; i < uniforms.size(); i++) {
- Uniform uniform = uniforms.getValue(i);
- uniform.reset(); // e.g check location again
- }
- }
-
- public void updateTexImageData(Image img, Texture.Type type) {
- int texId = img.getId();
- if (texId == -1) {
- // create texture
- JmeIosGLES.glGenTextures(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- texId = intBuf1[0];
- img.setId(texId);
- objManager.registerObject(img);
-
- statistics.onNewTexture();
- }
-
- // bind texture
- int target = convertTextureType(type);
- if (context.boundTextures[0] != img) {
- if (context.boundTextureUnit != 0) {
- JmeIosGLES.glActiveTexture(JmeIosGLES.GL_TEXTURE0);
- JmeIosGLES.checkGLError();
-
- context.boundTextureUnit = 0;
- }
-
- JmeIosGLES.glBindTexture(target, texId);
- JmeIosGLES.checkGLError();
-
- context.boundTextures[0] = img;
- }
-
- boolean needMips = false;
- if (img.isGeneratedMipmapsRequired()) {
- needMips = true;
- img.setMipmapsGenerated(true);
- }
-
- if (target == JmeIosGLES.GL_TEXTURE_CUBE_MAP) {
- // Check max texture size before upload
- if (img.getWidth() > maxCubeTexSize || img.getHeight() > maxCubeTexSize) {
- throw new RendererException("Cannot upload cubemap " + img + ". The maximum supported cubemap resolution is " + maxCubeTexSize);
- }
- } else {
- if (img.getWidth() > maxTexSize || img.getHeight() > maxTexSize) {
- throw new RendererException("Cannot upload texture " + img + ". The maximum supported texture resolution is " + maxTexSize);
- }
- }
-
- if (target == JmeIosGLES.GL_TEXTURE_CUBE_MAP) {
- // Upload a cube map / sky box
- /*
- @SuppressWarnings("unchecked")
- List bmps = (List) img.getEfficentData();
- if (bmps != null) {
- // Native android bitmap
- if (bmps.size() != 6) {
- throw new UnsupportedOperationException("Invalid texture: " + img
- + "Cubemap textures must contain 6 data units.");
- }
- for (int i = 0; i < 6; i++) {
- TextureUtil.uploadTextureBitmap(JmeIosGLES.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, bmps.get(i).getBitmap(), needMips);
- bmps.get(i).notifyBitmapUploaded();
- }
- } else {
- */
- // Standard jme3 image data
- List data = img.getData();
- if (data.size() != 6) {
- throw new UnsupportedOperationException("Invalid texture: " + img
- + "Cubemap textures must contain 6 data units.");
- }
- for (int i = 0; i < 6; i++) {
- TextureUtil.uploadTextureAny(img, JmeIosGLES.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, needMips);
- }
- //}
- } else {
- TextureUtil.uploadTextureAny(img, target, 0, needMips);
- /*
- if (img.getEfficentData() instanceof AndroidImageInfo) {
- AndroidImageInfo info = (AndroidImageInfo) img.getEfficentData();
- info.notifyBitmapUploaded();
- }
- */
- }
-
- img.clearUpdateNeeded();
- }
-
- private void setupTextureParams(Texture tex) {
- int target = convertTextureType(tex.getType());
-
- // filter things
- int minFilter = convertMinFilter(tex.getMinFilter());
- int magFilter = convertMagFilter(tex.getMagFilter());
-
- JmeIosGLES.glTexParameteri(target, JmeIosGLES.GL_TEXTURE_MIN_FILTER, minFilter);
- JmeIosGLES.checkGLError();
- JmeIosGLES.glTexParameteri(target, JmeIosGLES.GL_TEXTURE_MAG_FILTER, magFilter);
- JmeIosGLES.checkGLError();
-
- /*
- if (tex.getAnisotropicFilter() > 1){
-
- if (GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
- glTexParameterf(target,
- EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT,
- tex.getAnisotropicFilter());
- }
-
- }
- */
- // repeat modes
-
- switch (tex.getType()) {
- case ThreeDimensional:
- case CubeMap: // cubemaps use 3D coords
- // GL_TEXTURE_WRAP_R is not available in api 8
- //GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
- case TwoDimensional:
- case TwoDimensionalArray:
- JmeIosGLES.glTexParameteri(target, JmeIosGLES.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T)));
-
- // fall down here is intentional..
-// case OneDimensional:
- JmeIosGLES.glTexParameteri(target, JmeIosGLES.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S)));
-
- JmeIosGLES.checkGLError();
- break;
- default:
- throw new UnsupportedOperationException("Unknown texture type: " + tex.getType());
- }
-
- // R to Texture compare mode
-/*
- if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off){
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_MODE, GLES20.GL_COMPARE_R_TO_TEXTURE);
- GLES20.glTexParameteri(target, GLES20.GL_DEPTH_TEXTURE_MODE, GLES20.GL_INTENSITY);
- if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual){
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_GEQUAL);
- }else{
- GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_COMPARE_FUNC, GLES20.GL_LEQUAL);
- }
- }
- */
- }
-
- private int convertTextureType(Texture.Type type) {
- switch (type) {
- case TwoDimensional:
- return JmeIosGLES.GL_TEXTURE_2D;
- // case TwoDimensionalArray:
- // return EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT;
-// case ThreeDimensional:
- // return GLES20.GL_TEXTURE_3D;
- case CubeMap:
- return JmeIosGLES.GL_TEXTURE_CUBE_MAP;
- default:
- throw new UnsupportedOperationException("Unknown texture type: " + type);
- }
- }
-
- private int convertMagFilter(Texture.MagFilter filter) {
- switch (filter) {
- case Bilinear:
- return JmeIosGLES.GL_LINEAR;
- case Nearest:
- return JmeIosGLES.GL_NEAREST;
- default:
- throw new UnsupportedOperationException("Unknown mag filter: " + filter);
- }
- }
-
- private int convertMinFilter(Texture.MinFilter filter) {
- switch (filter) {
- case Trilinear:
- return JmeIosGLES.GL_LINEAR_MIPMAP_LINEAR;
- case BilinearNearestMipMap:
- return JmeIosGLES.GL_LINEAR_MIPMAP_NEAREST;
- case NearestLinearMipMap:
- return JmeIosGLES.GL_NEAREST_MIPMAP_LINEAR;
- case NearestNearestMipMap:
- return JmeIosGLES.GL_NEAREST_MIPMAP_NEAREST;
- case BilinearNoMipMaps:
- return JmeIosGLES.GL_LINEAR;
- case NearestNoMipMaps:
- return JmeIosGLES.GL_NEAREST;
- default:
- throw new UnsupportedOperationException("Unknown min filter: " + filter);
- }
- }
-
- private int convertWrapMode(Texture.WrapMode mode) {
- switch (mode) {
- case BorderClamp:
- case Clamp:
- case EdgeClamp:
- return JmeIosGLES.GL_CLAMP_TO_EDGE;
- case Repeat:
- return JmeIosGLES.GL_REPEAT;
- case MirroredRepeat:
- return JmeIosGLES.GL_MIRRORED_REPEAT;
- default:
- throw new UnsupportedOperationException("Unknown wrap mode: " + mode);
- }
- }
-
- private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
- for (VertexBuffer vb : mesh.getBufferList().getArray()) {
- if (vb.getBufferType() == Type.InterleavedData
- || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
- || vb.getBufferType() == Type.Index) {
- continue;
- }
-
- if (vb.getStride() == 0) {
- // not interleaved
- setVertexAttrib_Array(vb);
- } else {
- // interleaved
- VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
- setVertexAttrib_Array(vb, interleavedData);
- }
- }
-
- VertexBuffer indices = null;
- if (mesh.getNumLodLevels() > 0) {
- indices = mesh.getLodLevel(lod);
- } else {
- indices = mesh.getBuffer(Type.Index);//buffers.get(Type.Index.ordinal());
- }
- if (indices != null) {
- drawTriangleList_Array(indices, mesh, count);
- } else {
- JmeIosGLES.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
- JmeIosGLES.checkGLError();
- }
- clearVertexAttribs();
- }
-
- private void renderMeshDefault(Mesh mesh, int lod, int count) {
- VertexBuffer indices = null;
- VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
- if (interleavedData != null && interleavedData.isUpdateNeeded()) {
- updateBufferData(interleavedData);
- }
-
- //IntMap buffers = mesh.getBuffers(); ;
- if (mesh.getNumLodLevels() > 0) {
- indices = mesh.getLodLevel(lod);
- } else {
- indices = mesh.getBuffer(Type.Index);// buffers.get(Type.Index.ordinal());
- }
- for (VertexBuffer vb : mesh.getBufferList().getArray()){
- if (vb.getBufferType() == Type.InterleavedData
- || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
- || vb.getBufferType() == Type.Index) {
- continue;
- }
-
- if (vb.getStride() == 0) {
- // not interleaved
- setVertexAttrib(vb);
- } else {
- // interleaved
- setVertexAttrib(vb, interleavedData);
- }
- }
- if (indices != null) {
- drawTriangleList(indices, mesh, count);
- } else {
-// throw new UnsupportedOperationException("Cannot render without index buffer");
- JmeIosGLES.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
- JmeIosGLES.checkGLError();
- }
- clearVertexAttribs();
- }
-
-
- public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) {
- if (vb.getBufferType() == VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
- }
-
- if (vb.isUpdateNeeded() && idb == null) {
- updateBufferData(vb);
- }
-
- int programId = context.boundShaderProgram;
- if (programId > 0) {
- Attribute attrib = boundShader.getAttribute(vb.getBufferType());
- int loc = attrib.getLocation();
- if (loc == -1) {
- return; // not defined
- }
-
- if (loc == -2) {
-// stringBuf.setLength(0);
-// stringBuf.append("in").append(vb.getBufferType().name()).append('\0');
-// updateNameBuffer();
-
- String attributeName = "in" + vb.getBufferType().name();
- loc = JmeIosGLES.glGetAttribLocation(programId, attributeName);
- JmeIosGLES.checkGLError();
-
- // not really the name of it in the shader (inPosition\0) but
- // the internal name of the enum (Position).
- if (loc < 0) {
- attrib.setLocation(-1);
- return; // not available in shader.
- } else {
- attrib.setLocation(loc);
- }
- }
-
- VertexBuffer[] attribs = context.boundAttribs;
- if (!context.attribIndexList.moveToNew(loc)) {
- JmeIosGLES.glEnableVertexAttribArray(loc);
- JmeIosGLES.checkGLError();
- //System.out.println("Enabled ATTRIB IDX: "+loc);
- }
- if (attribs[loc] != vb) {
- // NOTE: Use id from interleaved buffer if specified
- int bufId = idb != null ? idb.getId() : vb.getId();
- assert bufId != -1;
-
- if (bufId == -1) {
- logger.warning("invalid buffer id");
- }
-
- if (context.boundArrayVBO != bufId) {
- JmeIosGLES.glBindBuffer(JmeIosGLES.GL_ARRAY_BUFFER, bufId);
- JmeIosGLES.checkGLError();
-
- context.boundArrayVBO = bufId;
- }
-
- vb.getData().rewind();
- /*
- Android22Workaround.glVertexAttribPointer(loc,
- vb.getNumComponents(),
- convertVertexBufferFormat(vb.getFormat()),
- vb.isNormalized(),
- vb.getStride(),
- 0);
- */
- logger.warning("iTODO Android22Workaround");
-
- JmeIosGLES.glVertexAttribPointer(loc,
- vb.getNumComponents(),
- convertVertexBufferFormat(vb.getFormat()),
- vb.isNormalized(),
- vb.getStride(),
- null);
-
- JmeIosGLES.checkGLError();
-
- attribs[loc] = vb;
- }
- } else {
- throw new IllegalStateException("Cannot render mesh without shader bound");
- }
- }
-
- public void setVertexAttrib(VertexBuffer vb) {
- setVertexAttrib(vb, null);
- }
-
- public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) {
- /* if (count > 1){
- ARBDrawInstanced.glDrawArraysInstancedARB(convertElementMode(mode), 0,
- vertCount, count);
- }else{*/
- JmeIosGLES.glDrawArrays(convertElementMode(mode), 0, vertCount);
- JmeIosGLES.checkGLError();
- /*
- }*/
- }
-
- public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) {
- if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
- }
-
- if (indexBuf.isUpdateNeeded()) {
- updateBufferData(indexBuf);
- }
-
- int bufId = indexBuf.getId();
- assert bufId != -1;
-
- if (bufId == -1) {
- throw new RendererException("Invalid buffer ID");
- }
-
- if (context.boundElementArrayVBO != bufId) {
- JmeIosGLES.glBindBuffer(JmeIosGLES.GL_ELEMENT_ARRAY_BUFFER, bufId);
- JmeIosGLES.checkGLError();
-
- context.boundElementArrayVBO = bufId;
- }
-
- int vertCount = mesh.getVertexCount();
- boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
-
- Buffer indexData = indexBuf.getData();
-
- if (!uintIndexSupport && (indexBuf.getFormat() == Format.UnsignedInt)) {
- throw new RendererException("OpenGL ES does not support 32-bit index buffers." +
- "Split your models to avoid going over 65536 vertices.");
- }
-
- if (mesh.getMode() == Mode.Hybrid) {
- int[] modeStart = mesh.getModeStart();
- int[] elementLengths = mesh.getElementLengths();
-
- int elMode = convertElementMode(Mode.Triangles);
- int fmt = convertVertexBufferFormat(indexBuf.getFormat());
- int elSize = indexBuf.getFormat().getComponentSize();
- int listStart = modeStart[0];
- int stripStart = modeStart[1];
- int fanStart = modeStart[2];
- int curOffset = 0;
- for (int i = 0; i < elementLengths.length; i++) {
- if (i == stripStart) {
- elMode = convertElementMode(Mode.TriangleStrip);
- } else if (i == fanStart) {
- elMode = convertElementMode(Mode.TriangleStrip);
- }
- int elementLength = elementLengths[i];
-
- if (useInstancing) {
- //ARBDrawInstanced.
- throw new IllegalArgumentException("instancing is not supported.");
- /*
- GLES20.glDrawElementsInstancedARB(elMode,
- elementLength,
- fmt,
- curOffset,
- count);
- */
- } else {
- indexBuf.getData().position(curOffset);
- JmeIosGLES.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
- JmeIosGLES.checkGLError();
- /*
- glDrawRangeElements(elMode,
- 0,
- vertCount,
- elementLength,
- fmt,
- curOffset);
- */
- }
-
- curOffset += elementLength * elSize;
- }
- } else {
- if (useInstancing) {
- throw new IllegalArgumentException("instancing is not supported.");
- //ARBDrawInstanced.
-/*
- GLES20.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertVertexBufferFormat(indexBuf.getFormat()),
- 0,
- count);
- */
- } else {
- logger.log(Level.FINE, "IGLESShaderRenderer drawTriangleList TODO check");
- indexData.rewind();
- JmeIosGLES.glDrawElementsIndex(
- convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertVertexBufferFormat(indexBuf.getFormat()),
- 0);
- /*TODO:
- indexData.rewind();
- JmeIosGLES.glDrawElements(
- convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertVertexBufferFormat(indexBuf.getFormat()),
- 0);
- */
- JmeIosGLES.checkGLError();
- }
- }
- }
-
- public int convertElementMode(Mesh.Mode mode) {
- switch (mode) {
- case Points:
- return JmeIosGLES.GL_POINTS;
- case Lines:
- return JmeIosGLES.GL_LINES;
- case LineLoop:
- return JmeIosGLES.GL_LINE_LOOP;
- case LineStrip:
- return JmeIosGLES.GL_LINE_STRIP;
- case Triangles:
- return JmeIosGLES.GL_TRIANGLES;
- case TriangleFan:
- return JmeIosGLES.GL_TRIANGLE_FAN;
- case TriangleStrip:
- return JmeIosGLES.GL_TRIANGLE_STRIP;
- default:
- throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode);
- }
- }
-
-
- private int convertVertexBufferFormat(Format format) {
- switch (format) {
- case Byte:
- return JmeIosGLES.GL_BYTE;
- case UnsignedByte:
- return JmeIosGLES.GL_UNSIGNED_BYTE;
- case Short:
- return JmeIosGLES.GL_SHORT;
- case UnsignedShort:
- return JmeIosGLES.GL_UNSIGNED_SHORT;
- case Int:
- return JmeIosGLES.GL_INT;
- case UnsignedInt:
- return JmeIosGLES.GL_UNSIGNED_INT;
- /*
- case Half:
- return NVHalfFloat.GL_HALF_FLOAT_NV;
- // return ARBHalfFloatVertex.GL_HALF_FLOAT;
- */
- case Float:
- return JmeIosGLES.GL_FLOAT;
-// case Double:
-// return JmeIosGLES.GL_DOUBLE;
- default:
- throw new RuntimeException("Unknown buffer format.");
-
- }
- }
-
- public void clearVertexAttribs() {
- IDList attribList = context.attribIndexList;
- for (int i = 0; i < attribList.oldLen; i++) {
- int idx = attribList.oldList[i];
-
- JmeIosGLES.glDisableVertexAttribArray(idx);
- JmeIosGLES.checkGLError();
-
- context.boundAttribs[idx] = null;
- }
- context.attribIndexList.copyNewToOld();
- }
-
- public void updateFrameBuffer(FrameBuffer fb) {
- int id = fb.getId();
- if (id == -1) {
- // create FBO
- JmeIosGLES.glGenFramebuffers(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- id = intBuf1[0];
- fb.setId(id);
- objManager.registerObject(fb);
-
- statistics.onNewFrameBuffer();
- }
-
- if (context.boundFBO != id) {
- JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, id);
- JmeIosGLES.checkGLError();
-
- // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0
- context.boundDrawBuf = 0;
- context.boundFBO = id;
- }
-
- FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
- if (depthBuf != null) {
- updateFrameBufferAttachment(fb, depthBuf);
- }
-
- for (int i = 0; i < fb.getNumColorBuffers(); i++) {
- FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
- updateFrameBufferAttachment(fb, colorBuf);
- }
-
- fb.clearUpdateNeeded();
- }
-
-
- public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) {
- boolean needAttach;
- if (rb.getTexture() == null) {
- // if it hasn't been created yet, then attach is required.
- needAttach = rb.getId() == -1;
- updateRenderBuffer(fb, rb);
- } else {
- needAttach = false;
- updateRenderTexture(fb, rb);
- }
- if (needAttach) {
- JmeIosGLES.glFramebufferRenderbuffer(JmeIosGLES.GL_FRAMEBUFFER,
- convertAttachmentSlot(rb.getSlot()),
- JmeIosGLES.GL_RENDERBUFFER,
- rb.getId());
-
- JmeIosGLES.checkGLError();
- }
- }
-
-
- public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) {
- Texture tex = rb.getTexture();
- Image image = tex.getImage();
- if (image.isUpdateNeeded()) {
- updateTexImageData(image, tex.getType());
-
- // NOTE: For depth textures, sets nearest/no-mips mode
- // Required to fix "framebuffer unsupported"
- // for old NVIDIA drivers!
- setupTextureParams(tex);
- }
-
- JmeIosGLES.glFramebufferTexture2D(JmeIosGLES.GL_FRAMEBUFFER,
- convertAttachmentSlot(rb.getSlot()),
- convertTextureType(tex.getType()),
- image.getId(),
- 0);
-
- JmeIosGLES.checkGLError();
- }
-
-
- private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) {
- int id = rb.getId();
- if (id == -1) {
- JmeIosGLES.glGenRenderbuffers(1, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- id = intBuf1[0];
- rb.setId(id);
- }
-
- if (context.boundRB != id) {
- JmeIosGLES.glBindRenderbuffer(JmeIosGLES.GL_RENDERBUFFER, id);
- JmeIosGLES.checkGLError();
-
- context.boundRB = id;
- }
-
- if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) {
- throw new RendererException("Resolution " + fb.getWidth()
- + ":" + fb.getHeight() + " is not supported.");
- }
-
- TextureUtil.IosGLImageFormat imageFormat = TextureUtil.getImageFormat(rb.getFormat());
- if (imageFormat.renderBufferStorageFormat == 0) {
- throw new RendererException("The format '" + rb.getFormat() + "' cannot be used for renderbuffers.");
- }
-
-// if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) {
- if (fb.getSamples() > 1) {
-// // FIXME
- throw new RendererException("Multisample FrameBuffer is not supported yet.");
-// int samples = fb.getSamples();
-// if (maxFBOSamples < samples) {
-// samples = maxFBOSamples;
-// }
-// glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
-// samples,
-// glFmt.internalFormat,
-// fb.getWidth(),
-// fb.getHeight());
- } else {
- JmeIosGLES.glRenderbufferStorage(JmeIosGLES.GL_RENDERBUFFER,
- imageFormat.renderBufferStorageFormat,
- fb.getWidth(),
- fb.getHeight());
-
- JmeIosGLES.checkGLError();
- }
- }
-
-
- private int convertAttachmentSlot(int attachmentSlot) {
- // can also add support for stencil here
- if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
- return JmeIosGLES.GL_DEPTH_ATTACHMENT;
- } else if (attachmentSlot == 0) {
- return JmeIosGLES.GL_COLOR_ATTACHMENT0;
- } else {
- throw new UnsupportedOperationException("Android does not support multiple color attachments to an FBO");
- }
- }
-
-
- private void checkFrameBufferStatus(FrameBuffer fb) {
- try {
- checkFrameBufferError();
- } catch (IllegalStateException ex) {
- logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb);
- printRealFrameBufferInfo(fb);
- throw ex;
- }
- }
-
- private void checkFrameBufferError() {
- int status = JmeIosGLES.glCheckFramebufferStatus(JmeIosGLES.GL_FRAMEBUFFER);
- switch (status) {
- case JmeIosGLES.GL_FRAMEBUFFER_COMPLETE:
- break;
- case JmeIosGLES.GL_FRAMEBUFFER_UNSUPPORTED:
- //Choose different formats
- throw new IllegalStateException("Framebuffer object format is "
- + "unsupported by the video hardware.");
- case JmeIosGLES.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- throw new IllegalStateException("Framebuffer has erronous attachment.");
- case JmeIosGLES.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- throw new IllegalStateException("Framebuffer doesn't have any renderbuffers attached.");
- case JmeIosGLES.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- throw new IllegalStateException("Framebuffer attachments must have same dimensions.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
-// throw new IllegalStateException("Framebuffer attachments must have same formats.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
-// throw new IllegalStateException("Incomplete draw buffer.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
-// throw new IllegalStateException("Incomplete read buffer.");
-// case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
-// throw new IllegalStateException("Incomplete multisample buffer.");
- default:
- //Programming error; will fail on all hardware
- throw new IllegalStateException("Some video driver error "
- + "or programming error occured. "
- + "Framebuffer object status is invalid: " + status);
- }
- }
-
- private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name) {
- System.out.println("== Renderbuffer " + name + " ==");
- System.out.println("RB ID: " + rb.getId());
- System.out.println("Is proper? " + JmeIosGLES.glIsRenderbuffer(rb.getId()));
-
- int attachment = convertAttachmentSlot(rb.getSlot());
-
- //intBuf16.clear();
- JmeIosGLES.glGetFramebufferAttachmentParameteriv(JmeIosGLES.GL_FRAMEBUFFER,
- attachment, JmeIosGLES.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, intBuf16, 0);
- int type = intBuf16[0];
-
- //intBuf16.clear();
- JmeIosGLES.glGetFramebufferAttachmentParameteriv(JmeIosGLES.GL_FRAMEBUFFER,
- attachment, JmeIosGLES.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, intBuf16, 0);
- int rbName = intBuf16[0];
-
- switch (type) {
- case JmeIosGLES.GL_NONE:
- System.out.println("Type: None");
- break;
- case JmeIosGLES.GL_TEXTURE:
- System.out.println("Type: Texture");
- break;
- case JmeIosGLES.GL_RENDERBUFFER:
- System.out.println("Type: Buffer");
- System.out.println("RB ID: " + rbName);
- break;
- }
- }
-
- private void printRealFrameBufferInfo(FrameBuffer fb) {
-// boolean doubleBuffer = GLES20.glGetBooleanv(GLES20.GL_DOUBLEBUFFER);
- boolean doubleBuffer = false; // FIXME
-// String drawBuf = getTargetBufferName(glGetInteger(GL_DRAW_BUFFER));
-// String readBuf = getTargetBufferName(glGetInteger(GL_READ_BUFFER));
-
- int fbId = fb.getId();
- //intBuf16.clear();
-// int curDrawBinding = GLES20.glGetIntegerv(GLES20.GL_DRAW_FRAMEBUFFER_BINDING);
-// int curReadBinding = glGetInteger(ARBFramebufferObject.GL_READ_FRAMEBUFFER_BINDING);
-
- System.out.println("=== OpenGL FBO State ===");
- System.out.println("Context doublebuffered? " + doubleBuffer);
- System.out.println("FBO ID: " + fbId);
- System.out.println("Is proper? " + JmeIosGLES.glIsFramebuffer(fbId));
-// System.out.println("Is bound to draw? " + (fbId == curDrawBinding));
-// System.out.println("Is bound to read? " + (fbId == curReadBinding));
-// System.out.println("Draw buffer: " + drawBuf);
-// System.out.println("Read buffer: " + readBuf);
-
- if (context.boundFBO != fbId) {
- JmeIosGLES.glBindFramebuffer(JmeIosGLES.GL_FRAMEBUFFER, fbId);
- context.boundFBO = fbId;
- }
-
- if (fb.getDepthBuffer() != null) {
- printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth");
- }
- for (int i = 0; i < fb.getNumColorBuffers(); i++) {
- printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i);
- }
-
-
- }
-
- public void drawTriangleList_Array(VertexBuffer indexBuf, Mesh mesh, int count) {
- if (indexBuf.getBufferType() != VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
- }
-
- boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
- if (useInstancing) {
- throw new IllegalArgumentException("Caps.MeshInstancing is not supported.");
- }
-
- int vertCount = mesh.getVertexCount();
- Buffer indexData = indexBuf.getData();
- indexData.rewind();
-
- if (mesh.getMode() == Mode.Hybrid) {
- int[] modeStart = mesh.getModeStart();
- int[] elementLengths = mesh.getElementLengths();
-
- int elMode = convertElementMode(Mode.Triangles);
- int fmt = convertVertexBufferFormat(indexBuf.getFormat());
- int elSize = indexBuf.getFormat().getComponentSize();
- int listStart = modeStart[0];
- int stripStart = modeStart[1];
- int fanStart = modeStart[2];
- int curOffset = 0;
- for (int i = 0; i < elementLengths.length; i++) {
- if (i == stripStart) {
- elMode = convertElementMode(Mode.TriangleStrip);
- } else if (i == fanStart) {
- elMode = convertElementMode(Mode.TriangleFan);
- }
- int elementLength = elementLengths[i];
-
- indexBuf.getData().position(curOffset);
- JmeIosGLES.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
- JmeIosGLES.checkGLError();
-
- curOffset += elementLength * elSize;
- }
- } else {
- JmeIosGLES.glDrawElements(
- convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertVertexBufferFormat(indexBuf.getFormat()),
- indexBuf.getData());
- JmeIosGLES.checkGLError();
- }
- }
-
- public void setVertexAttrib_Array(VertexBuffer vb, VertexBuffer idb) {
- if (vb.getBufferType() == VertexBuffer.Type.Index) {
- throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
- }
-
- // Get shader
- int programId = context.boundShaderProgram;
- if (programId > 0) {
- VertexBuffer[] attribs = context.boundAttribs;
-
- Attribute attrib = boundShader.getAttribute(vb.getBufferType());
- int loc = attrib.getLocation();
- if (loc == -1) {
- //throw new IllegalArgumentException("Location is invalid for attrib: [" + vb.getBufferType().name() + "]");
- return;
- } else if (loc == -2) {
- String attributeName = "in" + vb.getBufferType().name();
-
- loc = JmeIosGLES.glGetAttribLocation(programId, attributeName);
- JmeIosGLES.checkGLError();
-
- if (loc < 0) {
- attrib.setLocation(-1);
- return; // not available in shader.
- } else {
- attrib.setLocation(loc);
- }
-
- } // if (loc == -2)
-
- if ((attribs[loc] != vb) || vb.isUpdateNeeded()) {
- // NOTE: Use data from interleaved buffer if specified
- VertexBuffer avb = idb != null ? idb : vb;
- avb.getData().rewind();
- avb.getData().position(vb.getOffset());
-
- // Upload attribute data
- JmeIosGLES.glVertexAttribPointer(loc,
- vb.getNumComponents(),
- convertVertexBufferFormat(vb.getFormat()),
- vb.isNormalized(),
- vb.getStride(),
- avb.getData());
-
- JmeIosGLES.checkGLError();
-
- JmeIosGLES.glEnableVertexAttribArray(loc);
- JmeIosGLES.checkGLError();
-
- attribs[loc] = vb;
- } // if (attribs[loc] != vb)
- } else {
- throw new IllegalStateException("Cannot render mesh without shader bound");
- }
- }
-
- public void setVertexAttrib_Array(VertexBuffer vb) {
- setVertexAttrib_Array(vb, null);
- }
-
-
- public void updateShaderSourceData(ShaderSource source) {
- int id = source.getId();
- if (id == -1) {
- // Create id
- id = JmeIosGLES.glCreateShader(convertShaderType(source.getType()));
- JmeIosGLES.checkGLError();
-
- if (id <= 0) {
- throw new RendererException("Invalid ID received when trying to create shader.");
- }
- source.setId(id);
- }
-
- if (!source.getLanguage().equals("GLSL100")) {
- throw new RendererException("This shader cannot run in OpenGL ES. "
- + "Only GLSL 1.0 shaders are supported.");
- }
-
- // upload shader source
- // merge the defines and source code
- byte[] definesCodeData = source.getDefines().getBytes();
- byte[] sourceCodeData = source.getSource().getBytes();
- ByteBuffer codeBuf = BufferUtils.createByteBuffer(definesCodeData.length
- + sourceCodeData.length);
- codeBuf.put(definesCodeData);
- codeBuf.put(sourceCodeData);
- codeBuf.flip();
-
- if (powerVr && source.getType() == ShaderType.Vertex) {
- // XXX: This is to fix a bug in old PowerVR, remove
- // when no longer applicable.
- JmeIosGLES.glShaderSource(
- id, source.getDefines()
- + source.getSource());
- } else {
- String precision ="";
- if (source.getType() == ShaderType.Fragment) {
- precision = "precision mediump float;\n";
- }
- JmeIosGLES.glShaderSource(
- id,
- precision
- +source.getDefines()
- + source.getSource());
- }
-// int range[] = new int[2];
-// int precision[] = new int[1];
-// GLES20.glGetShaderPrecisionFormat(GLES20.GL_VERTEX_SHADER, GLES20.GL_HIGH_FLOAT, range, 0, precision, 0);
-// System.out.println("PRECISION HIGH FLOAT VERTEX");
-// System.out.println("range "+range[0]+"," +range[1]);
-// System.out.println("precision "+precision[0]);
-
- JmeIosGLES.glCompileShader(id);
- JmeIosGLES.checkGLError();
-
- JmeIosGLES.glGetShaderiv(id, JmeIosGLES.GL_COMPILE_STATUS, intBuf1, 0);
- JmeIosGLES.checkGLError();
-
- boolean compiledOK = intBuf1[0] == JmeIosGLES.GL_TRUE;
- String infoLog = null;
-
- if (VALIDATE_SHADER || !compiledOK) {
- // even if compile succeeded, check
- // log for warnings
- JmeIosGLES.glGetShaderiv(id, JmeIosGLES.GL_INFO_LOG_LENGTH, intBuf1, 0);
- JmeIosGLES.checkGLError();
- infoLog = JmeIosGLES.glGetShaderInfoLog(id);
- }
-
- if (compiledOK) {
- if (infoLog != null) {
- logger.log(Level.FINE, "compile success: {0}, {1}", new Object[]{source.getName(), infoLog});
- } else {
- logger.log(Level.FINE, "compile success: {0}", source.getName());
- }
- source.clearUpdateNeeded();
- } else {
- logger.log(Level.WARNING, "Bad compile of:\n{0}",
- new Object[]{ShaderDebug.formatShaderSource(stringBuf.toString() + source.getDefines() + source.getSource())});
- if (infoLog != null) {
- throw new RendererException("compile error in: " + source + "\n" + infoLog);
- } else {
- throw new RendererException("compile error in: " + source + "\nerror: ");
- }
- }
- }
-
-
- public int convertShaderType(ShaderType type) {
- switch (type) {
- case Fragment:
- return JmeIosGLES.GL_FRAGMENT_SHADER;
- case Vertex:
- return JmeIosGLES.GL_VERTEX_SHADER;
-// case Geometry:
-// return ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB;
- default:
- throw new RuntimeException("Unrecognized shader type.");
- }
- }
-
- private int convertTestFunction(RenderState.TestFunction testFunc) {
- switch (testFunc) {
- case Never:
- return JmeIosGLES.GL_NEVER;
- case Less:
- return JmeIosGLES.GL_LESS;
- case LessOrEqual:
- return JmeIosGLES.GL_LEQUAL;
- case Greater:
- return JmeIosGLES.GL_GREATER;
- case GreaterOrEqual:
- return JmeIosGLES.GL_GEQUAL;
- case Equal:
- return JmeIosGLES.GL_EQUAL;
- case NotEqual:
- return JmeIosGLES.GL_NOTEQUAL;
- case Always:
- return JmeIosGLES.GL_ALWAYS;
- default:
- throw new UnsupportedOperationException("Unrecognized test function: " + testFunc);
- }
- }
-
- public void setMainFrameBufferSrgb(boolean srgb) {
-
- }
-
- public void setLinearizeSrgbImages(boolean linearize) {
-
- }
-
- public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
- throw new UnsupportedOperationException("Not supported yet. URA will make that work seamlessly");
- }
-}
\ No newline at end of file
diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java
index d3276972d..a9398f159 100644
--- a/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java
+++ b/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java
@@ -34,6 +34,7 @@ package com.jme3.renderer.ios;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GLExt;
+import com.jme3.renderer.opengl.GLFbo;
import java.nio.Buffer;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
@@ -46,10 +47,13 @@ import java.nio.ShortBuffer;
*
* @author Kirill Vainer
*/
-public class IosGL implements GL, GLExt {
+public class IosGL implements GL, GLExt, GLFbo {
private final int[] temp_array = new int[16];
+ public void resetStats() {
+ }
+
private static int getLimitBytes(ByteBuffer buffer) {
checkLimit(buffer);
return buffer.limit();
@@ -90,7 +94,9 @@ public class IosGL implements GL, GLExt {
if (buffer.remaining() < n) {
throw new BufferOverflowException();
}
+ int pos = buffer.position();
buffer.put(array, 0, n);
+ buffer.position(pos);
}
private static void checkLimit(Buffer buffer) {
diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/TextureUtil.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/TextureUtil.java
deleted file mode 100644
index d11308c2a..000000000
--- a/jme3-ios/src/main/java/com/jme3/renderer/ios/TextureUtil.java
+++ /dev/null
@@ -1,576 +0,0 @@
-package com.jme3.renderer.ios;
-
-//import android.graphics.Bitmap;
-//import android.opengl.ETC1;
-//import android.opengl.ETC1Util.ETC1Texture;
-//import android.opengl.JmeIosGLES;
-//import android.opengl.GLUtils;
-//import com.jme3.asset.AndroidImageInfo;
-import com.jme3.renderer.ios.JmeIosGLES;
-import com.jme3.math.FastMath;
-import com.jme3.renderer.RendererException;
-import com.jme3.texture.Image;
-import com.jme3.texture.Image.Format;
-import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class TextureUtil {
-
- private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
- //TODO Make this configurable through appSettings
- public static boolean ENABLE_COMPRESSION = true;
- private static boolean NPOT = false;
- private static boolean ETC1support = false;
- private static boolean DXT1 = false;
- private static boolean PVRTC = false;
- private static boolean DEPTH24_STENCIL8 = false;
- private static boolean DEPTH_TEXTURE = false;
- private static boolean RGBA8 = false;
-
- // Same constant used by both GL_ARM_rgba8 and GL_OES_rgb8_rgba8.
- private static final int GL_RGBA8 = 0x8058;
-
- private static final int GL_DXT1 = 0x83F0;
- private static final int GL_DXT1A = 0x83F1;
-
- private static final int GL_DEPTH_STENCIL_OES = 0x84F9;
- private static final int GL_UNSIGNED_INT_24_8_OES = 0x84FA;
- private static final int GL_DEPTH24_STENCIL8_OES = 0x88F0;
-
- public static void loadTextureFeatures(String extensionString) {
- ETC1support = extensionString.contains("GL_OES_compressed_ETC1_RGB8_texture");
- DEPTH24_STENCIL8 = extensionString.contains("GL_OES_packed_depth_stencil");
- NPOT = extensionString.contains("GL_IMG_texture_npot")
- || extensionString.contains("GL_OES_texture_npot")
- || extensionString.contains("GL_NV_texture_npot_2D_mipmap");
-
- PVRTC = extensionString.contains("GL_IMG_texture_compression_pvrtc");
-
- DXT1 = extensionString.contains("GL_EXT_texture_compression_dxt1");
- DEPTH_TEXTURE = extensionString.contains("GL_OES_depth_texture");
-
- RGBA8 = extensionString.contains("GL_ARM_rgba8") ||
- extensionString.contains("GL_OES_rgb8_rgba8");
-
- logger.log(Level.FINE, "Supports ETC1? {0}", ETC1support);
- logger.log(Level.FINE, "Supports DEPTH24_STENCIL8? {0}", DEPTH24_STENCIL8);
- logger.log(Level.FINE, "Supports NPOT? {0}", NPOT);
- logger.log(Level.FINE, "Supports PVRTC? {0}", PVRTC);
- logger.log(Level.FINE, "Supports DXT1? {0}", DXT1);
- logger.log(Level.FINE, "Supports DEPTH_TEXTURE? {0}", DEPTH_TEXTURE);
- logger.log(Level.FINE, "Supports RGBA8? {0}", RGBA8);
- }
-
- /*
- private static void buildMipmap(Bitmap bitmap, boolean compress) {
- int level = 0;
- int height = bitmap.getHeight();
- int width = bitmap.getWidth();
-
- logger.log(Level.FINEST, " - Generating mipmaps for bitmap using SOFTWARE");
-
- JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
-
- while (height >= 1 || width >= 1) {
- //First of all, generate the texture from our bitmap and set it to the according level
- if (compress) {
- logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) with compression.", new Object[]{level, width, height});
- uploadBitmapAsCompressed(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, false, 0, 0);
- } else {
- logger.log(Level.FINEST, " - Uploading LOD level {0} ({1}x{2}) directly.", new Object[]{level, width, height});
- GLUtils.texImage2D(JmeIosGLES.GL_TEXTURE_2D, level, bitmap, 0);
- }
-
- if (height == 1 || width == 1) {
- break;
- }
-
- //Increase the mipmap level
- height /= 2;
- width /= 2;
- Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true);
-
- // Recycle any bitmaps created as a result of scaling the bitmap.
- // Do not recycle the original image (mipmap level 0)
- if (level != 0) {
- bitmap.recycle();
- }
-
- bitmap = bitmap2;
-
- level++;
- }
- }
-
- private static void uploadBitmapAsCompressed(int target, int level, Bitmap bitmap, boolean subTexture, int x, int y) {
- if (bitmap.hasAlpha()) {
- logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present.");
- if (subTexture) {
- GLUtils.texSubImage2D(target, level, x, y, bitmap);
- JmeIosGLES.checkGLError();
- } else {
- GLUtils.texImage2D(target, level, bitmap, 0);
- JmeIosGLES.checkGLError();
- }
- } else {
- // Convert to RGB565
- int bytesPerPixel = 2;
- Bitmap rgb565 = bitmap.copy(Bitmap.Config.RGB_565, true);
-
- // Put texture data into ByteBuffer
- ByteBuffer inputImage = BufferUtils.createByteBuffer(bitmap.getRowBytes() * bitmap.getHeight());
- rgb565.copyPixelsToBuffer(inputImage);
- inputImage.position(0);
-
- // Delete the copied RGB565 image
- rgb565.recycle();
-
- // Encode the image into the output bytebuffer
- int encodedImageSize = ETC1.getEncodedDataSize(bitmap.getWidth(), bitmap.getHeight());
- ByteBuffer compressedImage = BufferUtils.createByteBuffer(encodedImageSize);
- ETC1.encodeImage(inputImage, bitmap.getWidth(),
- bitmap.getHeight(),
- bytesPerPixel,
- bytesPerPixel * bitmap.getWidth(),
- compressedImage);
-
- // Delete the input image buffer
- BufferUtils.destroyDirectBuffer(inputImage);
-
- // Create an ETC1Texture from the compressed image data
- ETC1Texture etc1tex = new ETC1Texture(bitmap.getWidth(), bitmap.getHeight(), compressedImage);
-
- // Upload the ETC1Texture
- if (bytesPerPixel == 2) {
- int oldSize = (bitmap.getRowBytes() * bitmap.getHeight());
- int newSize = compressedImage.capacity();
- logger.log(Level.FINEST, " - Uploading compressed image to GL, oldSize = {0}, newSize = {1}, ratio = {2}", new Object[]{oldSize, newSize, (float) oldSize / newSize});
- if (subTexture) {
- JmeIosGLES.glCompressedTexSubImage2D(target,
- level,
- x, y,
- bitmap.getWidth(),
- bitmap.getHeight(),
- ETC1.ETC1_RGB8_OES,
- etc1tex.getData().capacity(),
- etc1tex.getData());
-
- JmeIosGLES.checkGLError();
- } else {
- JmeIosGLES.glCompressedTexImage2D(target,
- level,
- ETC1.ETC1_RGB8_OES,
- bitmap.getWidth(),
- bitmap.getHeight(),
- 0,
- etc1tex.getData().capacity(),
- etc1tex.getData());
-
- JmeIosGLES.checkGLError();
- }
-
-// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
-// JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5, etc1Texture);
-// } else if (bytesPerPixel == 3) {
-// ETC1Util.loadTexture(target, level, 0, JmeIosGLES.GL_RGB,
-// JmeIosGLES.GL_UNSIGNED_BYTE, etc1Texture);
- }
-
- BufferUtils.destroyDirectBuffer(compressedImage);
- }
- }
-
- /**
- * uploadTextureBitmap uploads a native android bitmap
- */
- /*
- public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips) {
- uploadTextureBitmap(target, bitmap, needMips, false, 0, 0);
- }
-
- /**
- * uploadTextureBitmap uploads a native android bitmap
- */
- /*
- public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean needMips, boolean subTexture, int x, int y) {
- boolean recycleBitmap = false;
- //TODO, maybe this should raise an exception when NPOT is not supported
-
- boolean willCompress = ENABLE_COMPRESSION && ETC1support && !bitmap.hasAlpha();
- if (needMips && willCompress) {
- // Image is compressed and mipmaps are desired, generate them
- // using software.
- buildMipmap(bitmap, willCompress);
- } else {
- if (willCompress) {
- // Image is compressed but mipmaps are not desired, upload directly.
- logger.log(Level.FINEST, " - Uploading compressed bitmap. Mipmaps are not generated.");
- uploadBitmapAsCompressed(target, 0, bitmap, subTexture, x, y);
-
- } else {
- // Image is not compressed, mipmaps may or may not be desired.
- logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
- (needMips
- ? " Mipmaps will be generated in HARDWARE"
- : " Mipmaps are not generated."));
- if (subTexture) {
- System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight());
- GLUtils.texSubImage2D(target, 0, x, y, bitmap);
- JmeIosGLES.checkGLError();
- } else {
- GLUtils.texImage2D(target, 0, bitmap, 0);
- JmeIosGLES.checkGLError();
- }
-
- if (needMips) {
- // No pregenerated mips available,
- // generate from base level if required
- JmeIosGLES.glGenerateMipmap(target);
- JmeIosGLES.checkGLError();
- }
- }
- }
-
- if (recycleBitmap) {
- bitmap.recycle();
- }
- }
- */
-
- public static void uploadTextureAny(Image img, int target, int index, boolean needMips) {
- /*
- if (img.getEfficentData() instanceof AndroidImageInfo) {
- logger.log(Level.FINEST, " === Uploading image {0}. Using BITMAP PATH === ", img);
- // If image was loaded from asset manager, use fast path
- AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
- uploadTextureBitmap(target, imageInfo.getBitmap(), needMips);
- } else {
- */
- logger.log(Level.FINEST, " === Uploading image {0}. Using BUFFER PATH === ", img);
- boolean wantGeneratedMips = needMips && !img.hasMipmaps();
- if (wantGeneratedMips && img.getFormat().isCompressed()) {
- logger.log(Level.WARNING, "Generating mipmaps is only"
- + " supported for Bitmap based or non-compressed images!");
- }
-
- // Upload using slower path
- logger.log(Level.FINEST, " - Uploading bitmap directly.{0}",
- (wantGeneratedMips
- ? " Mipmaps will be generated in HARDWARE"
- : " Mipmaps are not generated."));
-
- uploadTexture(img, target, index);
-
- // Image was uploaded using slower path, since it is not compressed,
- // then compress it
- if (wantGeneratedMips) {
- // No pregenerated mips available,
- // generate from base level if required
- JmeIosGLES.glGenerateMipmap(target);
- JmeIosGLES.checkGLError();
- }
- //}
- }
-
- private static void unsupportedFormat(Format fmt) {
- throw new UnsupportedOperationException("The image format '" + fmt + "' is unsupported by the video hardware.");
- }
-
- public static IosGLImageFormat getImageFormat(Format fmt) throws UnsupportedOperationException {
- IosGLImageFormat imageFormat = new IosGLImageFormat();
- switch (fmt) {
- case Depth32:
- case Depth32F:
- throw new UnsupportedOperationException("The image format '"
- + fmt + "' is not supported by OpenGL ES 2.0 specification.");
- case Alpha8:
- imageFormat.format = JmeIosGLES.GL_ALPHA;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- if (RGBA8) {
- imageFormat.renderBufferStorageFormat = GL_RGBA8;
- } else {
- // Highest precision alpha supported by vanilla OGLES2
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
- }
- break;
- case Luminance8:
- imageFormat.format = JmeIosGLES.GL_LUMINANCE;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- if (RGBA8) {
- imageFormat.renderBufferStorageFormat = GL_RGBA8;
- } else {
- // Highest precision luminance supported by vanilla OGLES2
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
- }
- break;
- case Luminance8Alpha8:
- imageFormat.format = JmeIosGLES.GL_LUMINANCE_ALPHA;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- if (RGBA8) {
- imageFormat.renderBufferStorageFormat = GL_RGBA8;
- } else {
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
- }
- break;
- case RGB565:
- imageFormat.format = JmeIosGLES.GL_RGB;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_6_5;
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
- break;
- case RGB5A1:
- imageFormat.format = JmeIosGLES.GL_RGBA;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT_5_5_5_1;
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB5_A1;
- break;
- case RGB8:
- imageFormat.format = JmeIosGLES.GL_RGB;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- if (RGBA8) {
- imageFormat.renderBufferStorageFormat = GL_RGBA8;
- } else {
- // Fallback: Use RGB565 if RGBA8 is not available.
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
- }
- break;
- case BGR8:
- imageFormat.format = JmeIosGLES.GL_RGB;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- if (RGBA8) {
- imageFormat.renderBufferStorageFormat = GL_RGBA8;
- } else {
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGB565;
- }
- break;
- case RGBA8:
- imageFormat.format = JmeIosGLES.GL_RGBA;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- if (RGBA8) {
- imageFormat.renderBufferStorageFormat = GL_RGBA8;
- } else {
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_RGBA4;
- }
- break;
- case Depth:
- case Depth16:
- if (!DEPTH_TEXTURE) {
- unsupportedFormat(fmt);
- }
- imageFormat.format = JmeIosGLES.GL_DEPTH_COMPONENT;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT;
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_DEPTH_COMPONENT16;
- break;
- case Depth24:
- case Depth24Stencil8:
- if (!DEPTH_TEXTURE) {
- unsupportedFormat(fmt);
- }
- if (DEPTH24_STENCIL8) {
- // NEW: True Depth24 + Stencil8 format.
- imageFormat.format = GL_DEPTH_STENCIL_OES;
- imageFormat.dataType = GL_UNSIGNED_INT_24_8_OES;
- imageFormat.renderBufferStorageFormat = GL_DEPTH24_STENCIL8_OES;
- } else {
- // Vanilla OGLES2, only Depth16 available.
- imageFormat.format = JmeIosGLES.GL_DEPTH_COMPONENT;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_SHORT;
- imageFormat.renderBufferStorageFormat = JmeIosGLES.GL_DEPTH_COMPONENT16;
- }
- break;
- case DXT1:
- if (!DXT1) {
- unsupportedFormat(fmt);
- }
- imageFormat.format = GL_DXT1;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- imageFormat.compress = true;
- break;
- case DXT1A:
- if (!DXT1) {
- unsupportedFormat(fmt);
- }
- imageFormat.format = GL_DXT1A;
- imageFormat.dataType = JmeIosGLES.GL_UNSIGNED_BYTE;
- imageFormat.compress = true;
- break;
- default:
- throw new UnsupportedOperationException("Unrecognized format: " + fmt);
- }
- return imageFormat;
- }
-
- public static class IosGLImageFormat {
-
- boolean compress = false;
- int format = -1;
- int renderBufferStorageFormat = -1;
- int dataType = -1;
- }
-
- private static void uploadTexture(Image img,
- int target,
- int index) {
-
- /*
- if (img.getEfficentData() instanceof AndroidImageInfo) {
- throw new RendererException("This image uses efficient data. "
- + "Use uploadTextureBitmap instead.");
- }
- */
-
- // Otherwise upload image directly.
- // Prefer to only use power of 2 textures here to avoid errors.
- Image.Format fmt = img.getFormat();
- ByteBuffer data;
- if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
- data = img.getData(index);
- } else {
- data = null;
- }
-
- int width = img.getWidth();
- int height = img.getHeight();
-
- if (!NPOT && img.isNPOT()) {
- // Check if texture is POT
- throw new RendererException("Non-power-of-2 textures "
- + "are not supported by the video hardware "
- + "and no scaling path available for image: " + img);
- }
- IosGLImageFormat imageFormat = getImageFormat(fmt);
-
- if (data != null) {
- JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
- JmeIosGLES.checkGLError();
- }
-
- int[] mipSizes = img.getMipMapSizes();
- int pos = 0;
- if (mipSizes == null) {
- if (data != null) {
- mipSizes = new int[]{data.capacity()};
- } else {
- mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
- }
- }
-
- for (int i = 0; i < mipSizes.length; i++) {
- int mipWidth = Math.max(1, width >> i);
- int mipHeight = Math.max(1, height >> i);
-
- if (data != null) {
- data.position(pos);
- data.limit(pos + mipSizes[i]);
- }
-
- if (imageFormat.compress && data != null) {
- JmeIosGLES.glCompressedTexImage2D(target,
- i,
- imageFormat.format,
- mipWidth,
- mipHeight,
- 0,
- data.remaining(),
- data);
- } else {
- JmeIosGLES.glTexImage2D(target,
- i,
- imageFormat.format,
- mipWidth,
- mipHeight,
- 0,
- imageFormat.format,
- imageFormat.dataType,
- data);
- }
- JmeIosGLES.checkGLError();
-
- pos += mipSizes[i];
- }
- }
-
- /**
- * Update the texture currently bound to target at with data from the given
- * Image at position x and y. The parameter index is used as the zoffset in
- * case a 3d texture or texture 2d array is being updated.
- *
- * @param image Image with the source data (this data will be put into the
- * texture)
- * @param target the target texture
- * @param index the mipmap level to update
- * @param x the x position where to put the image in the texture
- * @param y the y position where to put the image in the texture
- */
- public static void uploadSubTexture(
- Image img,
- int target,
- int index,
- int x,
- int y) {
- //TODO:
- /*
- if (img.getEfficentData() instanceof AndroidImageInfo) {
- AndroidImageInfo imageInfo = (AndroidImageInfo) img.getEfficentData();
- uploadTextureBitmap(target, imageInfo.getBitmap(), true, true, x, y);
- return;
- }
- */
-
- // Otherwise upload image directly.
- // Prefer to only use power of 2 textures here to avoid errors.
- Image.Format fmt = img.getFormat();
- ByteBuffer data;
- if (index >= 0 || img.getData() != null && img.getData().size() > 0) {
- data = img.getData(index);
- } else {
- data = null;
- }
-
- int width = img.getWidth();
- int height = img.getHeight();
-
- if (!NPOT && img.isNPOT()) {
- // Check if texture is POT
- throw new RendererException("Non-power-of-2 textures "
- + "are not supported by the video hardware "
- + "and no scaling path available for image: " + img);
- }
- IosGLImageFormat imageFormat = getImageFormat(fmt);
-
- if (data != null) {
- JmeIosGLES.glPixelStorei(JmeIosGLES.GL_UNPACK_ALIGNMENT, 1);
- JmeIosGLES.checkGLError();
- }
-
- int[] mipSizes = img.getMipMapSizes();
- int pos = 0;
- if (mipSizes == null) {
- if (data != null) {
- mipSizes = new int[]{data.capacity()};
- } else {
- mipSizes = new int[]{width * height * fmt.getBitsPerPixel() / 8};
- }
- }
-
- for (int i = 0; i < mipSizes.length; i++) {
- int mipWidth = Math.max(1, width >> i);
- int mipHeight = Math.max(1, height >> i);
-
- if (data != null) {
- data.position(pos);
- data.limit(pos + mipSizes[i]);
- }
-
- if (imageFormat.compress && data != null) {
- JmeIosGLES.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data);
- JmeIosGLES.checkGLError();
- } else {
- JmeIosGLES.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data);
- JmeIosGLES.checkGLError();
- }
-
- pos += mipSizes[i];
- }
- }
-}
diff --git a/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java b/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java
index 7feaeae5b..2d046550e 100644
--- a/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java
+++ b/jme3-ios/src/main/java/com/jme3/system/ios/IGLESContext.java
@@ -40,6 +40,7 @@ import com.jme3.renderer.ios.IosGL;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GLDebugES;
import com.jme3.renderer.opengl.GLExt;
+import com.jme3.renderer.opengl.GLFbo;
import com.jme3.renderer.opengl.GLRenderer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
@@ -158,11 +159,11 @@ public class IGLESContext implements JmeContext {
GLExt glext = (GLExt) gl;
// if (settings.getBoolean("GraphicsDebug")) {
- gl = new GLDebugES(gl, glext);
+ gl = new GLDebugES(gl, glext, (GLFbo) glext);
glext = (GLExt) gl;
// }
- renderer = new GLRenderer(gl, glext);
+ renderer = new GLRenderer(gl, glext, (GLFbo) glext);
renderer.initialize();
input = new IosInputHandler();
diff --git a/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java b/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java
index 4ec649086..e47de3a50 100644
--- a/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java
+++ b/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java
@@ -33,6 +33,7 @@ package com.jme3.system.ios;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader;
+import com.jme3.asset.TextureKey;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import java.io.IOException;
@@ -45,14 +46,16 @@ import java.io.InputStream;
public class IosImageLoader implements AssetLoader {
public Object load(AssetInfo info) throws IOException {
- InputStream in = info.openStream();
+ boolean flip = ((TextureKey) info.getKey()).isFlipY();
Image img = null;
+ InputStream in = null;
try {
- img = loadImageData(Image.Format.RGBA8, in);
- } catch (Exception e) {
- e.printStackTrace();
+ in = info.openStream();
+ img = loadImageData(Format.RGBA8, flip, in);
} finally {
- in.close();
+ if (in != null) {
+ in.close();
+ }
}
return img;
}
@@ -64,5 +67,5 @@ public class IosImageLoader implements AssetLoader {
* @param inputStream the InputStream to load the image data from
* @return the loaded Image
*/
- private static native Image loadImageData(Format format, InputStream inputStream);
+ private static native Image loadImageData(Format format, boolean flipY, InputStream inputStream);
}
diff --git a/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java b/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java
index 904db71cf..d29f76f62 100644
--- a/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java
+++ b/jme3-ios/src/main/java/com/jme3/system/ios/JmeIosSystem.java
@@ -36,6 +36,14 @@ import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext;
import com.jme3.system.JmeSystemDelegate;
import com.jme3.system.NullContext;
+import com.jme3.audio.AudioRenderer;
+import com.jme3.audio.ios.IosAL;
+import com.jme3.audio.ios.IosALC;
+//import com.jme3.audio.ios.IosEFX;
+import com.jme3.audio.openal.AL;
+import com.jme3.audio.openal.ALAudioRenderer;
+import com.jme3.audio.openal.ALC;
+import com.jme3.audio.openal.EFX;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
@@ -89,8 +97,11 @@ public class JmeIosSystem extends JmeSystemDelegate {
@Override
public AudioRenderer newAudioRenderer(AppSettings settings) {
- return null;
- }
+ ALC alc = new IosALC();
+ AL al = new IosAL();
+ //EFX efx = new IosEFX();
+ return new ALAudioRenderer(al, alc, null);
+ }
@Override
public void initialize(AppSettings settings) {
diff --git a/jme3-ios/src/main/resources/com/jme3/asset/IOS.cfg b/jme3-ios/src/main/resources/com/jme3/asset/IOS.cfg
new file mode 100644
index 000000000..e9d79a459
--- /dev/null
+++ b/jme3-ios/src/main/resources/com/jme3/asset/IOS.cfg
@@ -0,0 +1,10 @@
+INCLUDE com/jme3/asset/General.cfg
+
+# IOS specific loaders
+LOADER com.jme3.system.ios.IosImageLoader : jpg, bmp, gif, png, jpeg
+LOADER com.jme3.audio.plugins.OGGLoader : ogg
+LOADER com.jme3.material.plugins.J3MLoader : j3m
+LOADER com.jme3.material.plugins.J3MLoader : j3md
+LOADER com.jme3.shader.plugins.GLSLLoader : vert, frag, glsl, glsllib
+LOADER com.jme3.export.binary.BinaryImporter : j3o
+LOADER com.jme3.font.plugins.BitmapFontLoader : fnt
diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java
index 82b8ee72b..bf99c84eb 100644
--- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java
+++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java
@@ -13,7 +13,7 @@ import java.nio.ShortBuffer;
import com.jme3.renderer.opengl.GL4;
import org.lwjgl.opengl.*;
-public class LwjglGL implements GL, GL2, GL3,GL4 {
+public class LwjglGL implements GL, GL2, GL3, GL4 {
private static void checkLimit(Buffer buffer) {
if (buffer == null) {
@@ -27,6 +27,9 @@ public class LwjglGL implements GL, GL2, GL3,GL4 {
}
}
+ public void resetStats() {
+ }
+
public void glActiveTexture(int param1) {
GL13.glActiveTexture(param1);
}
@@ -237,6 +240,10 @@ public class LwjglGL implements GL, GL2, GL3,GL4 {
public String glGetString(int param1) {
return GL11.glGetString(param1);
}
+
+ public String glGetString(int param1, int param2) {
+ return GL30.glGetStringi(param1, param2);
+ }
public boolean glIsEnabled(int param1) {
return GL11.glIsEnabled(param1);
@@ -444,4 +451,10 @@ public class LwjglGL implements GL, GL2, GL3,GL4 {
public void glPatchParameter(int count) {
GL40.glPatchParameteri(GL40.GL_PATCH_VERTICES,count);
}
+
+ @Override
+ public void glDeleteVertexArrays(IntBuffer arrays) {
+ checkLimit(arrays);
+ ARBVertexArrayObject.glDeleteVertexArrays(arrays);
+ }
}
diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java
index 89139282a..2c6a63fd7 100644
--- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java
+++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLExt.java
@@ -7,12 +7,8 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.opengl.ARBDrawInstanced;
import org.lwjgl.opengl.ARBInstancedArrays;
-import org.lwjgl.opengl.ARBPixelBufferObject;
import org.lwjgl.opengl.ARBSync;
import org.lwjgl.opengl.ARBTextureMultisample;
-import org.lwjgl.opengl.EXTFramebufferBlit;
-import org.lwjgl.opengl.EXTFramebufferMultisample;
-import org.lwjgl.opengl.EXTFramebufferObject;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GLSync;
@@ -30,99 +26,51 @@ public class LwjglGLExt implements GLExt {
throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
}
}
-
- public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
- EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
- }
+ @Override
public void glBufferData(int target, IntBuffer data, int usage) {
checkLimit(data);
GL15.glBufferData(target, data, usage);
}
+ @Override
public void glBufferSubData(int target, long offset, IntBuffer data) {
checkLimit(data);
GL15.glBufferSubData(target, offset, data);
}
+ @Override
public void glDrawArraysInstancedARB(int mode, int first, int count, int primcount) {
ARBDrawInstanced.glDrawArraysInstancedARB(mode, first, count, primcount);
}
+ @Override
public void glDrawBuffers(IntBuffer bufs) {
checkLimit(bufs);
GL20.glDrawBuffers(bufs);
}
+ @Override
public void glDrawElementsInstancedARB(int mode, int indices_count, int type, long indices_buffer_offset, int primcount) {
ARBDrawInstanced.glDrawElementsInstancedARB(mode, indices_count, type, indices_buffer_offset, primcount);
}
+ @Override
public void glGetMultisample(int pname, int index, FloatBuffer val) {
checkLimit(val);
ARBTextureMultisample.glGetMultisample(pname, index, val);
}
- public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
- EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
- }
-
+ @Override
public void glTexImage2DMultisample(int target, int samples, int internalformat, int width, int height, boolean fixedsamplelocations) {
ARBTextureMultisample.glTexImage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
}
+ @Override
public void glVertexAttribDivisorARB(int index, int divisor) {
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
}
- public void glBindFramebufferEXT(int param1, int param2) {
- EXTFramebufferObject.glBindFramebufferEXT(param1, param2);
- }
-
- public void glBindRenderbufferEXT(int param1, int param2) {
- EXTFramebufferObject.glBindRenderbufferEXT(param1, param2);
- }
-
- public int glCheckFramebufferStatusEXT(int param1) {
- return EXTFramebufferObject.glCheckFramebufferStatusEXT(param1);
- }
-
- public void glDeleteFramebuffersEXT(IntBuffer param1) {
- checkLimit(param1);
- EXTFramebufferObject.glDeleteFramebuffersEXT(param1);
- }
-
- public void glDeleteRenderbuffersEXT(IntBuffer param1) {
- checkLimit(param1);
- EXTFramebufferObject.glDeleteRenderbuffersEXT(param1);
- }
-
- public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
- EXTFramebufferObject.glFramebufferRenderbufferEXT(param1, param2, param3, param4);
- }
-
- public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
- EXTFramebufferObject.glFramebufferTexture2DEXT(param1, param2, param3, param4, param5);
- }
-
- public void glGenFramebuffersEXT(IntBuffer param1) {
- checkLimit(param1);
- EXTFramebufferObject.glGenFramebuffersEXT(param1);
- }
-
- public void glGenRenderbuffersEXT(IntBuffer param1) {
- checkLimit(param1);
- EXTFramebufferObject.glGenRenderbuffersEXT(param1);
- }
-
- public void glGenerateMipmapEXT(int param1) {
- EXTFramebufferObject.glGenerateMipmapEXT(param1);
- }
-
- public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
- EXTFramebufferObject.glRenderbufferStorageEXT(param1, param2, param3, param4);
- }
-
@Override
public Object glFenceSync(int condition, int flags) {
return ARBSync.glFenceSync(condition, flags);
diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java
new file mode 100644
index 000000000..159000a6c
--- /dev/null
+++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboEXT.java
@@ -0,0 +1,98 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GLFbo;
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+import org.lwjgl.opengl.EXTFramebufferBlit;
+import org.lwjgl.opengl.EXTFramebufferMultisample;
+import org.lwjgl.opengl.EXTFramebufferObject;
+
+/**
+ * Implements GLFbo via GL_EXT_framebuffer_object.
+ *
+ * @author Kirill Vainer
+ */
+public class LwjglGLFboEXT implements GLFbo {
+
+ private static void checkLimit(Buffer buffer) {
+ if (buffer == null) {
+ return;
+ }
+ if (buffer.limit() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+ }
+ if (buffer.remaining() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+ }
+ }
+
+ @Override
+ public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
+ EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ }
+
+ @Override
+ public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
+ EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+ }
+
+ @Override
+ public void glBindFramebufferEXT(int param1, int param2) {
+ EXTFramebufferObject.glBindFramebufferEXT(param1, param2);
+ }
+
+ @Override
+ public void glBindRenderbufferEXT(int param1, int param2) {
+ EXTFramebufferObject.glBindRenderbufferEXT(param1, param2);
+ }
+
+ @Override
+ public int glCheckFramebufferStatusEXT(int param1) {
+ return EXTFramebufferObject.glCheckFramebufferStatusEXT(param1);
+ }
+
+ @Override
+ public void glDeleteFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glDeleteFramebuffersEXT(param1);
+ }
+
+ @Override
+ public void glDeleteRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glDeleteRenderbuffersEXT(param1);
+ }
+
+ @Override
+ public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
+ EXTFramebufferObject.glFramebufferRenderbufferEXT(param1, param2, param3, param4);
+ }
+
+ @Override
+ public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
+ EXTFramebufferObject.glFramebufferTexture2DEXT(param1, param2, param3, param4, param5);
+ }
+
+ @Override
+ public void glGenFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glGenFramebuffersEXT(param1);
+ }
+
+ @Override
+ public void glGenRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ EXTFramebufferObject.glGenRenderbuffersEXT(param1);
+ }
+
+ @Override
+ public void glGenerateMipmapEXT(int param1) {
+ EXTFramebufferObject.glGenerateMipmapEXT(param1);
+ }
+
+ @Override
+ public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
+ EXTFramebufferObject.glRenderbufferStorageEXT(param1, param2, param3, param4);
+ }
+}
diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java
new file mode 100644
index 000000000..acc540273
--- /dev/null
+++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGLFboGL3.java
@@ -0,0 +1,96 @@
+package com.jme3.renderer.lwjgl;
+
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.opengl.GLFbo;
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+import org.lwjgl.opengl.GL30;
+
+/**
+ * Implements GLFbo via OpenGL3+.
+ *
+ * @author Kirill Vainer
+ */
+public class LwjglGLFboGL3 implements GLFbo {
+
+ private static void checkLimit(Buffer buffer) {
+ if (buffer == null) {
+ return;
+ }
+ if (buffer.limit() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (limit = 0), that's an error");
+ }
+ if (buffer.remaining() == 0) {
+ throw new RendererException("Attempting to upload empty buffer (remaining = 0), that's an error");
+ }
+ }
+
+ @Override
+ public void glBlitFramebufferEXT(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
+ GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
+ }
+
+ @Override
+ public void glRenderbufferStorageMultisampleEXT(int target, int samples, int internalformat, int width, int height) {
+ GL30.glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
+ }
+
+ @Override
+ public void glBindFramebufferEXT(int param1, int param2) {
+ GL30.glBindFramebuffer(param1, param2);
+ }
+
+ @Override
+ public void glBindRenderbufferEXT(int param1, int param2) {
+ GL30.glBindRenderbuffer(param1, param2);
+ }
+
+ @Override
+ public int glCheckFramebufferStatusEXT(int param1) {
+ return GL30.glCheckFramebufferStatus(param1);
+ }
+
+ @Override
+ public void glDeleteFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glDeleteFramebuffers(param1);
+ }
+
+ @Override
+ public void glDeleteRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glDeleteRenderbuffers(param1);
+ }
+
+ @Override
+ public void glFramebufferRenderbufferEXT(int param1, int param2, int param3, int param4) {
+ GL30.glFramebufferRenderbuffer(param1, param2, param3, param4);
+ }
+
+ @Override
+ public void glFramebufferTexture2DEXT(int param1, int param2, int param3, int param4, int param5) {
+ GL30.glFramebufferTexture2D(param1, param2, param3, param4, param5);
+ }
+
+ @Override
+ public void glGenFramebuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glGenFramebuffers(param1);
+ }
+
+ @Override
+ public void glGenRenderbuffersEXT(IntBuffer param1) {
+ checkLimit(param1);
+ GL30.glGenRenderbuffers(param1);
+ }
+
+ @Override
+ public void glGenerateMipmapEXT(int param1) {
+ GL30.glGenerateMipmap(param1);
+ }
+
+ @Override
+ public void glRenderbufferStorageEXT(int param1, int param2, int param3, int param4) {
+ GL30.glRenderbufferStorage(param1, param2, param3, param4);
+ }
+}
diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
index 1286323ef..c88f7b734 100644
--- a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
+++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java
@@ -39,13 +39,18 @@ import com.jme3.renderer.Renderer;
import com.jme3.renderer.RendererException;
import com.jme3.renderer.lwjgl.LwjglGL;
import com.jme3.renderer.lwjgl.LwjglGLExt;
+import com.jme3.renderer.lwjgl.LwjglGLFboEXT;
+import com.jme3.renderer.lwjgl.LwjglGLFboGL3;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GL2;
import com.jme3.renderer.opengl.GL3;
+import com.jme3.renderer.opengl.GL4;
import com.jme3.renderer.opengl.GLDebugDesktop;
import com.jme3.renderer.opengl.GLExt;
import com.jme3.renderer.opengl.GLFbo;
import com.jme3.renderer.opengl.GLRenderer;
+import com.jme3.renderer.opengl.GLTiming;
+import com.jme3.renderer.opengl.GLTimingState;
import com.jme3.renderer.opengl.GLTracer;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext;
@@ -203,28 +208,44 @@ public abstract class LwjglContext implements JmeContext {
}
if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2)
- || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) {
+ || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)) {
GL gl = new LwjglGL();
- GLFbo glfbo = new LwjglGLExt();
+ GLExt glext = new LwjglGLExt();
+ GLFbo glfbo;
+
+ if (GLContext.getCapabilities().OpenGL30) {
+ glfbo = new LwjglGLFboGL3();
+ } else {
+ glfbo = new LwjglGLFboEXT();
+ }
if (settings.getBoolean("GraphicsDebug")) {
- gl = new GLDebugDesktop(gl, glfbo);
+ gl = new GLDebugDesktop(gl, glext, glfbo);
+ glext = (GLExt) gl;
glfbo = (GLFbo) gl;
}
+ if (settings.getBoolean("GraphicsTiming")) {
+ GLTimingState timingState = new GLTimingState();
+ gl = (GL) GLTiming.createGLTiming(gl, timingState, GL.class, GL2.class, GL3.class, GL4.class);
+ glext = (GLExt) GLTiming.createGLTiming(glext, timingState, GLExt.class);
+ glfbo = (GLFbo) GLTiming.createGLTiming(glfbo, timingState, GLFbo.class);
+ }
+
if (settings.getBoolean("GraphicsTrace")) {
- gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class);
- glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLExt.class);
+ gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class, GL4.class);
+ glext = (GLExt) GLTracer.createDesktopGlTracer(glext, GLExt.class);
+ glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLFbo.class);
}
- renderer = new GLRenderer(gl, glfbo);
+ renderer = new GLRenderer(gl, glext, glfbo);
renderer.initialize();
} else {
throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer());
}
if (GLContext.getCapabilities().GL_ARB_debug_output && settings.getBoolean("GraphicsDebug")) {
- ARBDebugOutput.glDebugMessageCallbackARB(new ARBDebugOutputCallback());
+ ARBDebugOutput.glDebugMessageCallbackARB(new ARBDebugOutputCallback(new LwjglGLDebugOutputHandler()));
}
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection());
diff --git a/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java
new file mode 100644
index 000000000..c8f329f13
--- /dev/null
+++ b/jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglGLDebugOutputHandler.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009-2015 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.system.lwjgl;
+
+import java.util.HashMap;
+import org.lwjgl.opengl.ARBDebugOutput;
+import org.lwjgl.opengl.ARBDebugOutputCallback;
+
+class LwjglGLDebugOutputHandler implements ARBDebugOutputCallback.Handler {
+
+ private static final HashMap constMap = new HashMap();
+ private static final String MESSAGE_FORMAT =
+ "[JME3] OpenGL debug message\r\n" +
+ " ID: %d\r\n" +
+ " Source: %s\r\n" +
+ " Type: %s\r\n" +
+ " Severity: %s\r\n" +
+ " Message: %s";
+
+ static {
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_API_ARB, "API");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_APPLICATION_ARB, "APPLICATION");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_OTHER_ARB, "OTHER");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_SHADER_COMPILER_ARB, "SHADER_COMPILER");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_THIRD_PARTY_ARB, "THIRD_PARTY");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB, "WINDOW_SYSTEM");
+
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, "DEPRECATED_BEHAVIOR");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_ERROR_ARB, "ERROR");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_OTHER_ARB, "OTHER");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_PERFORMANCE_ARB, "PERFORMANCE");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_PORTABILITY_ARB, "PORTABILITY");
+ constMap.put(ARBDebugOutput.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, "UNDEFINED_BEHAVIOR");
+
+ constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_HIGH_ARB, "HIGH");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_MEDIUM_ARB, "MEDIUM");
+ constMap.put(ARBDebugOutput.GL_DEBUG_SEVERITY_LOW_ARB, "LOW");
+ }
+
+ @Override
+ public void handleMessage(int source, int type, int id, int severity, String message) {
+ String sourceStr = constMap.get(source);
+ String typeStr = constMap.get(type);
+ String severityStr = constMap.get(severity);
+
+ System.err.println(String.format(MESSAGE_FORMAT, id, sourceStr, typeStr, severityStr, message));
+ }
+
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/Client.java b/jme3-networking/src/main/java/com/jme3/network/Client.java
index 6837a4d86..dd9873238 100644
--- a/jme3-networking/src/main/java/com/jme3/network/Client.java
+++ b/jme3-networking/src/main/java/com/jme3/network/Client.java
@@ -31,6 +31,8 @@
*/
package com.jme3.network;
+import com.jme3.network.service.ClientServiceManager;
+
/**
* Represents a remote connection to a server that can be used
@@ -72,6 +74,12 @@ public interface Client extends MessageConnection
* be able to connect to.
*/
public int getVersion();
+
+ /**
+ * Returns the manager for client services. Client services extend
+ * the functionality of the client.
+ */
+ public ClientServiceManager getServices();
/**
* Sends a message to the server.
diff --git a/jme3-networking/src/main/java/com/jme3/network/Server.java b/jme3-networking/src/main/java/com/jme3/network/Server.java
index 72926eab7..a52cb33bf 100644
--- a/jme3-networking/src/main/java/com/jme3/network/Server.java
+++ b/jme3-networking/src/main/java/com/jme3/network/Server.java
@@ -33,6 +33,8 @@ package com.jme3.network;
import java.util.Collection;
+import com.jme3.network.service.HostedServiceManager;
+
/**
* Represents a host that can send and receive messages to
* a set of remote client connections.
@@ -54,6 +56,12 @@ public interface Server
*/
public int getVersion();
+ /**
+ * Returns the manager for hosted services. Hosted services extend
+ * the functionality of the server.
+ */
+ public HostedServiceManager getServices();
+
/**
* Sends the specified message to all connected clients.
*/
diff --git a/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java b/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java
index 54e8fd7f9..c0cc2e616 100644
--- a/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java
+++ b/jme3-networking/src/main/java/com/jme3/network/base/DefaultClient.java
@@ -37,6 +37,8 @@ import com.jme3.network.kernel.Connector;
import com.jme3.network.message.ChannelInfoMessage;
import com.jme3.network.message.ClientRegistrationMessage;
import com.jme3.network.message.DisconnectMessage;
+import com.jme3.network.service.ClientServiceManager;
+import com.jme3.network.service.serializer.ClientSerializerRegistrationsService;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
@@ -54,7 +56,7 @@ import java.util.logging.Logger;
*/
public class DefaultClient implements Client
{
- static Logger log = Logger.getLogger(DefaultClient.class.getName());
+ static final Logger log = Logger.getLogger(DefaultClient.class.getName());
// First two channels are reserved for reliable and
// unreliable. Note: channels are endpoint specific so these
@@ -80,10 +82,14 @@ public class DefaultClient implements Client
private ConnectorFactory connectorFactory;
+ private ClientServiceManager services;
+
public DefaultClient( String gameName, int version )
{
this.gameName = gameName;
this.version = version;
+ this.services = new ClientServiceManager(this);
+ addStandardServices();
}
public DefaultClient( String gameName, int version, Connector reliable, Connector fast,
@@ -93,6 +99,10 @@ public class DefaultClient implements Client
setPrimaryConnectors( reliable, fast, connectorFactory );
}
+ protected void addStandardServices() {
+ services.addService(new ClientSerializerRegistrationsService());
+ }
+
protected void setPrimaryConnectors( Connector reliable, Connector fast, ConnectorFactory connectorFactory )
{
if( reliable == null )
@@ -200,6 +210,11 @@ public class DefaultClient implements Client
{
return version;
}
+
+ public ClientServiceManager getServices()
+ {
+ return services;
+ }
public void send( Message message )
{
@@ -260,7 +275,7 @@ public class DefaultClient implements Client
{
checkRunning();
- closeConnections( null );
+ closeConnections( null );
}
protected void closeConnections( DisconnectInfo info )
@@ -268,6 +283,10 @@ public class DefaultClient implements Client
if( !isRunning )
return;
+ // Let the services get a chance to stop before we
+ // kill the connection.
+ services.stop();
+
// Send a close message
// Tell the thread it's ok to die
@@ -285,6 +304,9 @@ public class DefaultClient implements Client
fireDisconnected(info);
isRunning = false;
+
+ // Terminate the services
+ services.terminate();
}
public void addClientStateListener( ClientStateListener listener )
@@ -329,6 +351,9 @@ public class DefaultClient implements Client
protected void fireConnected()
{
+ // Let the services know we are finally started
+ services.start();
+
for( ClientStateListener l : stateListeners ) {
l.clientConnected( this );
}
diff --git a/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java b/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java
index 3816fbace..0a9ac0ef1 100644
--- a/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java
+++ b/jme3-networking/src/main/java/com/jme3/network/base/DefaultServer.java
@@ -37,6 +37,8 @@ import com.jme3.network.kernel.Kernel;
import com.jme3.network.message.ChannelInfoMessage;
import com.jme3.network.message.ClientRegistrationMessage;
import com.jme3.network.message.DisconnectMessage;
+import com.jme3.network.service.HostedServiceManager;
+import com.jme3.network.service.serializer.ServerSerializerRegistrationsService;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
@@ -55,7 +57,7 @@ import java.util.logging.Logger;
*/
public class DefaultServer implements Server
{
- static Logger log = Logger.getLogger(DefaultServer.class.getName());
+ static final Logger log = Logger.getLogger(DefaultServer.class.getName());
// First two channels are reserved for reliable and
// unreliable
@@ -85,6 +87,8 @@ public class DefaultServer implements Server
= new MessageListenerRegistry();
private List connectionListeners = new CopyOnWriteArrayList();
+ private HostedServiceManager services;
+
public DefaultServer( String gameName, int version, Kernel reliable, Kernel fast )
{
if( reliable == null )
@@ -92,6 +96,8 @@ public class DefaultServer implements Server
this.gameName = gameName;
this.version = version;
+ this.services = new HostedServiceManager(this);
+ addStandardServices();
reliableAdapter = new KernelAdapter( this, reliable, dispatcher, true );
channels.add( reliableAdapter );
@@ -101,6 +107,10 @@ public class DefaultServer implements Server
}
}
+ protected void addStandardServices() {
+ services.addService(new ServerSerializerRegistrationsService());
+ }
+
public String getGameName()
{
return gameName;
@@ -110,6 +120,11 @@ public class DefaultServer implements Server
{
return version;
}
+
+ public HostedServiceManager getServices()
+ {
+ return services;
+ }
public int addChannel( int port )
{
@@ -164,7 +179,10 @@ public class DefaultServer implements Server
ka.start();
}
- isRunning = true;
+ isRunning = true;
+
+ // Start the services
+ services.start();
}
public boolean isRunning()
@@ -177,13 +195,20 @@ public class DefaultServer implements Server
if( !isRunning )
throw new IllegalStateException( "Server is not started." );
+ // First stop the services since we are about to
+ // kill the connections they are using
+ services.stop();
+
try {
// Kill the adpaters, they will kill the kernels
for( KernelAdapter ka : channels ) {
ka.close();
}
- isRunning = false;
+ isRunning = false;
+
+ // Now terminate all of the services
+ services.terminate();
} catch( InterruptedException e ) {
throw new RuntimeException( "Interrupted while closing", e );
}
@@ -396,6 +421,18 @@ public class DefaultServer implements Server
return endpointConnections.get(endpoint);
}
+ protected void removeConnecting( Endpoint p )
+ {
+ // No easy lookup for connecting Connections
+ // from endpoint.
+ for( Map.Entry e : connecting.entrySet() ) {
+ if( e.getValue().hasEndpoint(p) ) {
+ connecting.remove(e.getKey());
+ return;
+ }
+ }
+ }
+
protected void connectionClosed( Endpoint p )
{
if( p.isConnected() ) {
@@ -411,10 +448,10 @@ public class DefaultServer implements Server
// Also note: this method will be called multiple times per
// HostedConnection if it has multiple endpoints.
- Connection removed = null;
+ Connection removed;
synchronized( this ) {
// Just in case the endpoint was still connecting
- connecting.values().remove(p);
+ removeConnecting(p);
// And the regular management
removed = (Connection)endpointConnections.remove(p);
@@ -452,6 +489,16 @@ public class DefaultServer implements Server
id = nextId.getAndIncrement();
channels = new Endpoint[channelCount];
}
+
+ boolean hasEndpoint( Endpoint p )
+ {
+ for( Endpoint e : channels ) {
+ if( p == e ) {
+ return true;
+ }
+ }
+ return false;
+ }
void setChannel( int channel, Endpoint p )
{
@@ -557,6 +604,7 @@ public class DefaultServer implements Server
return Collections.unmodifiableSet(sessionData.keySet());
}
+ @Override
public String toString()
{
return "Connection[ id=" + id + ", reliable=" + channels[CH_RELIABLE]
diff --git a/jme3-networking/src/main/java/com/jme3/network/message/SerializerRegistrationsMessage.java b/jme3-networking/src/main/java/com/jme3/network/message/SerializerRegistrationsMessage.java
new file mode 100644
index 000000000..da7f2a7cf
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/message/SerializerRegistrationsMessage.java
@@ -0,0 +1,188 @@
+/*
+ * $Id: SerializerRegistrationsMessage.java 3829 2014-11-24 07:25:43Z pspeed $
+ *
+ * Copyright (c) 2012, Paul Speed
+ * All rights reserved.
+ */
+
+package com.jme3.network.message;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.SerializerRegistration;
+import com.jme3.network.serializing.serializers.FieldSerializer;
+import java.util.*;
+import java.util.jar.Attributes;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Holds a compiled set of message registration information that
+ * can be sent over the wire. The received message can then be
+ * used to register all of the classes using the same IDs and
+ * same ordering, etc.. The intent is that the server compiles
+ * this message once it is sure that all serializable classes have
+ * been registered. It can then send this to each new client and
+ * they can use it to register all of the classes without requiring
+ * exactly reproducing the same calls that the server did to register
+ * messages.
+ *
+ *
Normally, JME recommends that apps have a common utility method
+ * that they call on both client and server. However, this makes
+ * pluggable services nearly impossible as some central class has to
+ * know about all registered serializers. This message implementation
+ * gets around by only requiring registration on the server.
+ *
+ * @author Paul Speed
+ */
+@Serializable
+public class SerializerRegistrationsMessage extends AbstractMessage {
+
+ static final Logger log = Logger.getLogger(SerializerRegistrationsMessage.class.getName());
+
+ public static final Set ignore = new HashSet();
+ static {
+ // We could build this automatically but then we
+ // risk making a client and server out of date simply because
+ // their JME versions are out of date.
+ ignore.add(Boolean.class);
+ ignore.add(Float.class);
+ ignore.add(Boolean.class);
+ ignore.add(Byte.class);
+ ignore.add(Character.class);
+ ignore.add(Short.class);
+ ignore.add(Integer.class);
+ ignore.add(Long.class);
+ ignore.add(Float.class);
+ ignore.add(Double.class);
+ ignore.add(String.class);
+
+ ignore.add(DisconnectMessage.class);
+ ignore.add(ClientRegistrationMessage.class);
+
+ ignore.add(Date.class);
+ ignore.add(AbstractCollection.class);
+ ignore.add(AbstractList.class);
+ ignore.add(AbstractSet.class);
+ ignore.add(ArrayList.class);
+ ignore.add(HashSet.class);
+ ignore.add(LinkedHashSet.class);
+ ignore.add(LinkedList.class);
+ ignore.add(TreeSet.class);
+ ignore.add(Vector.class);
+ ignore.add(AbstractMap.class);
+ ignore.add(Attributes.class);
+ ignore.add(HashMap.class);
+ ignore.add(Hashtable.class);
+ ignore.add(IdentityHashMap.class);
+ ignore.add(TreeMap.class);
+ ignore.add(WeakHashMap.class);
+ ignore.add(Enum.class);
+
+ ignore.add(GZIPCompressedMessage.class);
+ ignore.add(ZIPCompressedMessage.class);
+
+ ignore.add(ChannelInfoMessage.class);
+
+ ignore.add(SerializerRegistrationsMessage.class);
+ ignore.add(SerializerRegistrationsMessage.Registration.class);
+ }
+
+ public static SerializerRegistrationsMessage INSTANCE;
+ public static Registration[] compiled;
+ private static final Serializer fieldSerializer = new FieldSerializer();
+
+ private Registration[] registrations;
+
+ public SerializerRegistrationsMessage() {
+ setReliable(true);
+ }
+
+ public SerializerRegistrationsMessage( Registration... registrations ) {
+ setReliable(true);
+ this.registrations = registrations;
+ }
+
+ public static void compile() {
+
+ // Let's just see what they are here
+ List list = new ArrayList();
+ for( SerializerRegistration reg : Serializer.getSerializerRegistrations() ) {
+ Class type = reg.getType();
+ if( ignore.contains(type) )
+ continue;
+ if( type.isPrimitive() )
+ continue;
+
+ list.add(new Registration(reg));
+ }
+
+ if( log.isLoggable(Level.FINE) ) {
+ log.log( Level.FINE, "Number of registered classes:{0}", list.size());
+ for( Registration reg : list ) {
+ log.log( Level.FINE, " {0}", reg);
+ }
+ }
+ compiled = list.toArray(new Registration[list.size()]);
+
+ INSTANCE = new SerializerRegistrationsMessage(compiled);
+ }
+
+ public void registerAll() {
+ for( Registration reg : registrations ) {
+ log.log( Level.INFO, "Registering:{0}", reg);
+ reg.register();
+ }
+ }
+
+ @Serializable
+ public static final class Registration {
+
+ private short id;
+ private String className;
+ private String serializerClassName;
+
+ public Registration() {
+ }
+
+ public Registration( SerializerRegistration reg ) {
+
+ this.id = reg.getId();
+ this.className = reg.getType().getName();
+ if( reg.getSerializer().getClass() != FieldSerializer.class ) {
+ this.serializerClassName = reg.getSerializer().getClass().getName();
+ }
+ }
+
+ public void register() {
+ try {
+ Class type = Class.forName(className);
+ Serializer serializer;
+ if( serializerClassName == null ) {
+ serializer = fieldSerializer;
+ } else {
+ Class serializerType = Class.forName(serializerClassName);
+ serializer = (Serializer)serializerType.newInstance();
+ }
+ SerializerRegistration result = Serializer.registerClassForId(id, type, serializer);
+ log.log( Level.FINE, " result:{0}", result);
+ } catch( ClassNotFoundException e ) {
+ throw new RuntimeException( "Class not found attempting to register:" + this, e );
+ } catch( InstantiationException e ) {
+ throw new RuntimeException( "Error instantiating serializer registering:" + this, e );
+ } catch( IllegalAccessException e ) {
+ throw new RuntimeException( "Error instantiating serializer registering:" + this, e );
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Registration[" + id + " = " + className + ", serializer=" + serializerClassName + "]";
+ }
+ }
+}
+
+
+
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/AbstractClientService.java b/jme3-networking/src/main/java/com/jme3/network/service/AbstractClientService.java
new file mode 100644
index 000000000..bd1836451
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/AbstractClientService.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+
+/**
+ * Convenient base class for ClientServices providing some default ClientService
+ * interface implementations as well as a few convenience methods
+ * such as getServiceManager() and getService(type). Subclasses
+ * must at least override the onInitialize() method to handle
+ * service initialization.
+ *
+ * @author Paul Speed
+ */
+public abstract class AbstractClientService extends AbstractService
+ implements ClientService {
+
+ protected AbstractClientService() {
+ }
+
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/AbstractHostedService.java b/jme3-networking/src/main/java/com/jme3/network/service/AbstractHostedService.java
new file mode 100644
index 000000000..789767b73
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/AbstractHostedService.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+import com.jme3.network.HostedConnection;
+import com.jme3.network.Server;
+
+
+/**
+ * Convenient base class for HostedServices providing some default HostedService
+ * interface implementations as well as a few convenience methods
+ * such as getServiceManager() and getService(type). Subclasses
+ * must at least override the onInitialize() method to handle
+ * service initialization.
+ *
+ * @author Paul Speed
+ */
+public abstract class AbstractHostedService extends AbstractService
+ implements HostedService {
+
+ protected AbstractHostedService() {
+ }
+
+ /**
+ * Default implementation does nothing. Implementations can
+ * override this to peform custom new connection behavior.
+ */
+ @Override
+ public void connectionAdded(Server server, HostedConnection hc) {
+ }
+
+ /**
+ * Default implementation does nothing. Implementations can
+ * override this to peform custom leaving connection behavior.
+ */
+ @Override
+ public void connectionRemoved(Server server, HostedConnection hc) {
+ }
+
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/AbstractService.java b/jme3-networking/src/main/java/com/jme3/network/service/AbstractService.java
new file mode 100644
index 000000000..84df3d81b
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/AbstractService.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+
+/**
+ * Base class providing some default Service interface implementations
+ * as well as a few convenience methods such as getServiceManager()
+ * and getService(type). Subclasses must at least override the
+ * onInitialize() method to handle service initialization.
+ *
+ * @author Paul Speed
+ */
+public abstract class AbstractService implements Service {
+
+ private S serviceManager;
+
+ protected AbstractService() {
+ }
+
+ /**
+ * Returns the ServiceManager that was passed to
+ * initialize() during service initialization.
+ */
+ protected S getServiceManager() {
+ return serviceManager;
+ }
+
+ /**
+ * Retrieves the first sibling service of the specified
+ * type.
+ */
+ protected > T getService( Class type ) {
+ return type.cast(serviceManager.getService(type));
+ }
+
+ /**
+ * Initializes this service by keeping a reference to
+ * the service manager and calling onInitialize().
+ */
+ @Override
+ public final void initialize( S serviceManager ) {
+ this.serviceManager = serviceManager;
+ onInitialize(serviceManager);
+ }
+
+ /**
+ * Called during initialize() for the subclass to perform
+ * implementation specific initialization.
+ */
+ protected abstract void onInitialize( S serviceManager );
+
+ /**
+ * Default implementation does nothing. Implementations can
+ * override this to peform custom startup behavior.
+ */
+ @Override
+ public void start() {
+ }
+
+ /**
+ * Default implementation does nothing. Implementations can
+ * override this to peform custom stop behavior.
+ */
+ @Override
+ public void stop() {
+ }
+
+ /**
+ * Default implementation does nothing. Implementations can
+ * override this to peform custom termination behavior.
+ */
+ @Override
+ public void terminate( S serviceManager ) {
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[serviceManager=" + serviceManager + "]";
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/ClientService.java b/jme3-networking/src/main/java/com/jme3/network/service/ClientService.java
new file mode 100644
index 000000000..c8057cb31
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/ClientService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+
+/**
+ * Interface implemented by Client-side services that augment
+ * a network Client's functionality.
+ *
+ * @author Paul Speed
+ */
+public interface ClientService extends Service {
+
+ /**
+ * Called when the service is first attached to the service
+ * manager.
+ */
+ @Override
+ public void initialize( ClientServiceManager serviceManager );
+
+ /**
+ * Called when the service manager is started or if the
+ * service is added to an already started service manager.
+ */
+ @Override
+ public void start();
+
+ /**
+ * Called when the service is shutting down. All services
+ * are stopped and any service manager resources are closed
+ * before the services are terminated.
+ */
+ @Override
+ public void stop();
+
+ /**
+ * The service manager is fully shutting down. All services
+ * have been stopped and related connections closed.
+ */
+ @Override
+ public void terminate( ClientServiceManager serviceManager );
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/ClientServiceManager.java b/jme3-networking/src/main/java/com/jme3/network/service/ClientServiceManager.java
new file mode 100644
index 000000000..dc204fb2f
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/ClientServiceManager.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+import com.jme3.network.Client;
+
+
+/**
+ * Manages ClientServices on behalf of a network Client object.
+ *
+ * @author Paul Speed
+ */
+public class ClientServiceManager extends ServiceManager {
+
+ private Client client;
+
+ /**
+ * Creates a new ClientServiceManager for the specified network Client.
+ */
+ public ClientServiceManager( Client client ) {
+ this.client = client;
+ }
+
+ /**
+ * Returns the network Client associated with this ClientServiceManager.
+ */
+ public Client getClient() {
+ return client;
+ }
+
+ /**
+ * Returns 'this' and is what is passed to ClientService.initialize()
+ * and ClientService.termnate();
+ */
+ @Override
+ protected final ClientServiceManager getParent() {
+ return this;
+ }
+
+ /**
+ * Adds the specified ClientService and initializes it. If the service manager
+ * has already been started then the service will also be started.
+ */
+ public void addService( ClientService s ) {
+ super.addService(s);
+ }
+
+ /**
+ * Adds all of the specified ClientServices and initializes them. If the service manager
+ * has already been started then the services will also be started.
+ * This is a convenience method that delegates to addService(), thus each
+ * service will be initialized (and possibly started) in sequence rather
+ * than doing them all at the end.
+ */
+ public void addServices( ClientService... services ) {
+ for( ClientService s : services ) {
+ super.addService(s);
+ }
+ }
+
+ /**
+ * Removes the specified ClientService from this service manager, stopping
+ * and terminating it as required. If this service manager is in a
+ * started state then the service will be stopped. After removal,
+ * the service will be terminated.
+ */
+ public void removeService( ClientService s ) {
+ super.removeService(s);
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/HostedService.java b/jme3-networking/src/main/java/com/jme3/network/service/HostedService.java
new file mode 100644
index 000000000..f522b72d6
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/HostedService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+import com.jme3.network.ConnectionListener;
+
+
+/**
+ * Interface implemented by Server-side services that augment
+ * a network Server's functionality.
+ *
+ * @author Paul Speed
+ */
+public interface HostedService extends Service, ConnectionListener {
+
+ /**
+ * Called when the service is first attached to the service
+ * manager.
+ */
+ @Override
+ public void initialize( HostedServiceManager serviceManager );
+
+ /**
+ * Called when the service manager is started or if the
+ * service is added to an already started service manager.
+ */
+ @Override
+ public void start();
+
+ /**
+ * Called when the service is shutting down. All services
+ * are stopped and any service manager resources are closed
+ * before the services are terminated.
+ */
+ @Override
+ public void stop();
+
+ /**
+ * The service manager is fully shutting down. All services
+ * have been stopped and related connections closed.
+ */
+ @Override
+ public void terminate( HostedServiceManager serviceManager );
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/HostedServiceManager.java b/jme3-networking/src/main/java/com/jme3/network/service/HostedServiceManager.java
new file mode 100644
index 000000000..3606ba44b
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/HostedServiceManager.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+import com.jme3.network.ConnectionListener;
+import com.jme3.network.HostedConnection;
+import com.jme3.network.Server;
+
+
+/**
+ * Manages HostedServices on behalf of a network Server object.
+ * All HostedServices are automatically informed about new and
+ * leaving connections.
+ *
+ * @author Paul Speed
+ */
+public class HostedServiceManager extends ServiceManager {
+
+ private Server server;
+ private ConnectionObserver connectionObserver;
+
+ /**
+ * Creates a HostedServiceManager for the specified network Server.
+ */
+ public HostedServiceManager( Server server ) {
+ this.server = server;
+ this.connectionObserver = new ConnectionObserver();
+ server.addConnectionListener(connectionObserver);
+ }
+
+ /**
+ * Returns the network Server associated with this HostedServiceManager.
+ */
+ public Server getServer() {
+ return server;
+ }
+
+ /**
+ * Returns 'this' and is what is passed to HostedService.initialize()
+ * and HostedService.termnate();
+ */
+ @Override
+ protected final HostedServiceManager getParent() {
+ return this;
+ }
+
+ /**
+ * Adds the specified HostedService and initializes it. If the service manager
+ * has already been started then the service will also be started.
+ */
+ public void addService( HostedService s ) {
+ super.addService(s);
+ }
+
+ /**
+ * Adds all of the specified HostedServices and initializes them. If the service manager
+ * has already been started then the services will also be started.
+ * This is a convenience method that delegates to addService(), thus each
+ * service will be initialized (and possibly started) in sequence rather
+ * than doing them all at the end.
+ */
+ public void addServices( HostedService... services ) {
+ for( HostedService s : services ) {
+ super.addService(s);
+ }
+ }
+
+ /**
+ * Removes the specified HostedService from this service manager, stopping
+ * and terminating it as required. If this service manager is in a
+ * started state then the service will be stopped. After removal,
+ * the service will be terminated.
+ */
+ public void removeService( HostedService s ) {
+ super.removeService(s);
+ }
+
+ /**
+ * Called internally when a new connection has been added so that the
+ * services can be notified.
+ */
+ protected void addConnection( HostedConnection hc ) {
+ for( Service s : getServices() ) {
+ ((HostedService)s).connectionAdded(server, hc);
+ }
+ }
+
+ /**
+ * Called internally when a connection has been removed so that the
+ * services can be notified.
+ */
+ protected void removeConnection( HostedConnection hc ) {
+ for( Service s : getServices() ) {
+ ((HostedService)s).connectionRemoved(server, hc);
+ }
+ }
+
+ protected class ConnectionObserver implements ConnectionListener {
+
+ @Override
+ public void connectionAdded(Server server, HostedConnection hc) {
+ addConnection(hc);
+ }
+
+ @Override
+ public void connectionRemoved(Server server, HostedConnection hc) {
+ removeConnection(hc);
+ }
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/Service.java b/jme3-networking/src/main/java/com/jme3/network/service/Service.java
new file mode 100644
index 000000000..f56c90dda
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/Service.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+
+/**
+ * The base interface for managed services.
+ *
+ * @author Paul Speed
+ */
+public interface Service {
+
+ /**
+ * Called when the service is first attached to the service
+ * manager.
+ */
+ public void initialize( S serviceManager );
+
+ /**
+ * Called when the service manager is started or if the
+ * service is added to an already started service manager.
+ */
+ public void start();
+
+ /**
+ * Called when the service manager is shutting down. All services
+ * are stopped and any service manager resources are closed
+ * before the services are terminated.
+ */
+ public void stop();
+
+ /**
+ * The service manager is fully shutting down. All services
+ * have been stopped and related connections closed.
+ */
+ public void terminate( S serviceManager );
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/ServiceManager.java b/jme3-networking/src/main/java/com/jme3/network/service/ServiceManager.java
new file mode 100644
index 000000000..b8ee7d3c4
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/ServiceManager.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2015 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.network.service;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * The base service manager class from which the HostedServiceManager
+ * and ClientServiceManager classes are derived. This manages the
+ * the underlying services and their life cycles.
+ *
+ * @author Paul Speed
+ */
+public abstract class ServiceManager {
+
+ private List> services = new CopyOnWriteArrayList>();
+ private volatile boolean started = false;
+
+ protected ServiceManager() {
+ }
+
+ /**
+ * Retreives the 'parent' of this service manager, usually
+ * a more specifically typed version of 'this' but it can be
+ * anything the seervices are expecting.
+ */
+ protected abstract T getParent();
+
+ /**
+ * Returns the complete list of services managed by this
+ * service manager. This list is thread safe following the
+ * CopyOnWriteArrayList semantics.
+ */
+ protected List> getServices() {
+ return services;
+ }
+
+ /**
+ * Starts this service manager and all services that it contains.
+ * Any services added after the service manager has started will have
+ * their start() methods called.
+ */
+ public void start() {
+ if( started ) {
+ return;
+ }
+ for( Service s : services ) {
+ s.start();
+ }
+ started = true;
+ }
+
+ /**
+ * Returns true if this service manager has been started.
+ */
+ public boolean isStarted() {
+ return started;
+ }
+
+ /**
+ * Stops all services and puts the service manager into a stopped state.
+ */
+ public void stop() {
+ if( !started ) {
+ throw new IllegalStateException(getClass().getSimpleName() + " not started.");
+ }
+ for( Service s : services ) {
+ s.stop();
+ }
+ started = false;
+ }
+
+ /**
+ * Adds the specified service and initializes it. If the service manager
+ * has already been started then the service will also be started.
+ */
+ public > void addService( S s ) {
+ services.add(s);
+ s.initialize(getParent());
+ if( started ) {
+ s.start();
+ }
+ }
+
+ /**
+ * Removes the specified service from this service manager, stopping
+ * and terminating it as required. If this service manager is in a
+ * started state then the service will be stopped. After removal,
+ * the service will be terminated.
+ */
+ public > void removeService( S s ) {
+ if( started ) {
+ s.stop();
+ }
+ services.remove(s);
+ s.terminate(getParent());
+ }
+
+ /**
+ * Terminates all services. If the service manager has not been
+ * stopped yet then it will be stopped.
+ */
+ public void terminate() {
+ if( started ) {
+ stop();
+ }
+ for( Service s : services ) {
+ s.terminate(getParent());
+ }
+ }
+
+ /**
+ * Retrieves the first service of the specified type.
+ */
+ public > S getService( Class type ) {
+ for( Service s : services ) {
+ if( type.isInstance(s) ) {
+ return type.cast(s);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + "[services=" + services + "]";
+ }
+}
+
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcClientService.java b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcClientService.java
new file mode 100644
index 000000000..d9ea134e1
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcClientService.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015 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.network.service.rpc;
+
+import com.jme3.network.Client;
+import com.jme3.network.util.ObjectMessageDelegator;
+import com.jme3.network.service.AbstractClientService;
+import com.jme3.network.service.ClientServiceManager;
+
+
+/**
+ * RPC service that can be added to a network Client to
+ * add RPC send/receive capabilities. Remote procedure
+ * calls can be made to the server and responses retrieved.
+ * Any remote procedure calls that the server performs for
+ * this connection will be received by this service and delegated
+ * to the appropriate RpcHandlers.
+ *
+ * @author Paul Speed
+ */
+public class RpcClientService extends AbstractClientService {
+
+ private RpcConnection rpc;
+ private ObjectMessageDelegator delegator;
+
+ /**
+ * Creates a new RpcClientService that can be registered
+ * with the network Client object.
+ */
+ public RpcClientService() {
+ }
+
+ /**
+ * Used internally to setup the RpcConnection and MessageDelegator.
+ */
+ @Override
+ protected void onInitialize( ClientServiceManager serviceManager ) {
+ Client client = serviceManager.getClient();
+ this.rpc = new RpcConnection(client);
+
+ delegator = new ObjectMessageDelegator(rpc, true);
+ client.addMessageListener(delegator, delegator.getMessageTypes());
+ }
+
+ /**
+ * Used internally to unregister the RPC MessageDelegator that
+ * was previously added to the network Client.
+ */
+ @Override
+ public void terminate( ClientServiceManager serviceManager ) {
+ Client client = serviceManager.getClient();
+ client.removeMessageListener(delegator, delegator.getMessageTypes());
+ }
+
+ /**
+ * Performs a synchronous call on the server against the specified
+ * object using the specified procedure ID. Both inboud and outbound
+ * communication is done on the specified channel.
+ */
+ public Object callAndWait( byte channel, short objId, short procId, Object... args ) {
+ return rpc.callAndWait(channel, objId, procId, args);
+ }
+
+ /**
+ * Performs an asynchronous call on the server against the specified
+ * object using the specified procedure ID. Communication is done
+ * over the specified channel. No responses are received and none
+ * are waited for.
+ */
+ public void callAsync( byte channel, short objId, short procId, Object... args ) {
+ rpc.callAsync(channel, objId, procId, args);
+ }
+
+ /**
+ * Register a handler that will be called when the server
+ * performs a remove procedure call against this client.
+ * Only one handler per object ID can be registered at any given time,
+ * though the same handler can be registered for multiple object
+ * IDs.
+ */
+ public void registerHandler( short objId, RpcHandler handler ) {
+ rpc.registerHandler(objId, handler);
+ }
+
+ /**
+ * Removes a previously registered handler for the specified
+ * object ID.
+ */
+ public void removeHandler( short objId, RpcHandler handler ) {
+ rpc.removeHandler(objId, handler);
+ }
+
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcConnection.java b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcConnection.java
new file mode 100644
index 000000000..b78316bf3
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcConnection.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2015 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.network.service.rpc;
+
+import com.jme3.network.MessageConnection;
+import com.jme3.network.service.rpc.msg.RpcCallMessage;
+import com.jme3.network.service.rpc.msg.RpcResponseMessage;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Wraps a message connection to provide RPC call support. This
+ * is used internally by the RpcClientService and RpcHostedService to manage
+ * network messaging.
+ *
+ * @author Paul Speed
+ */
+public class RpcConnection {
+
+ static final Logger log = Logger.getLogger(RpcConnection.class.getName());
+
+ /**
+ * The underlying connection upon which RPC call messages are sent
+ * and RPC response messages are received. It can be a Client or
+ * a HostedConnection depending on the mode of the RPC service.
+ */
+ private MessageConnection connection;
+
+ /**
+ * The objectId index of RpcHandler objects that are used to perform the
+ * RPC calls for a particular object.
+ */
+ private Map handlers = new ConcurrentHashMap();
+
+ /**
+ * Provides unique messages IDs for outbound synchronous call
+ * messages. These are then used in the responses index to
+ * locate the proper ResponseHolder objects.
+ */
+ private AtomicLong sequenceNumber = new AtomicLong();
+
+ /**
+ * Tracks the ResponseHolder objects for sent message IDs. When the
+ * response is received, the appropriate handler is found here and the
+ * response or error set, thus releasing the waiting caller.
+ */
+ private Map responses = new ConcurrentHashMap();
+
+ /**
+ * Creates a new RpcConnection for the specified network connection.
+ */
+ public RpcConnection( MessageConnection connection ) {
+ this.connection = connection;
+ }
+
+ /**
+ * Clears any pending synchronous calls causing them to
+ * throw an exception with the message "Closing connection".
+ */
+ public void close() {
+ // Let any pending waits go free
+ for( ResponseHolder holder : responses.values() ) {
+ holder.release();
+ }
+ }
+
+ /**
+ * Performs a remote procedure call with the specified arguments and waits
+ * for the response. Both the outbound message and inbound response will
+ * be sent on the specified channel.
+ */
+ public Object callAndWait( byte channel, short objId, short procId, Object... args ) {
+
+ RpcCallMessage msg = new RpcCallMessage(sequenceNumber.getAndIncrement(),
+ channel, objId, procId, args);
+
+ // Need to register an object so we can wait for the response.
+ // ...before we send it. Just in case.
+ ResponseHolder holder = new ResponseHolder(msg);
+ responses.put(msg.getMessageId(), holder);
+
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "Sending:{0} on channel:{1}", new Object[]{msg, channel});
+ }
+ if( channel >= 0 ) {
+ connection.send(channel, msg);
+ } else {
+ connection.send(msg);
+ }
+
+ return holder.getResponse();
+ }
+
+ /**
+ * Performs a remote procedure call with the specified arguments but does
+ * not wait for a response. The outbound message is sent on the specified channel.
+ * There is no inbound response message.
+ */
+ public void callAsync( byte channel, short objId, short procId, Object... args ) {
+
+ RpcCallMessage msg = new RpcCallMessage(-1, channel, objId, procId, args);
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "Sending:{0} on channel:{1}", new Object[]{msg, channel});
+ }
+ connection.send(channel, msg);
+ }
+
+ /**
+ * Register a handler that can be called by the other end
+ * of the connection using the specified object ID. Only one
+ * handler per object ID can be registered at any given time,
+ * though the same handler can be registered for multiple object
+ * IDs.
+ */
+ public void registerHandler( short objId, RpcHandler handler ) {
+ handlers.put(objId, handler);
+ }
+
+ /**
+ * Removes a previously registered handler for the specified
+ * object ID.
+ */
+ public void removeHandler( short objId, RpcHandler handler ) {
+ RpcHandler removing = handlers.get(objId);
+ if( handler != removing ) {
+ throw new IllegalArgumentException("Handler not registered for object ID:"
+ + objId + ", handler:" + handler );
+ }
+ handlers.remove(objId);
+ }
+
+ protected void send( byte channel, RpcResponseMessage msg ) {
+ if( channel >= 0 ) {
+ connection.send(channel, msg);
+ } else {
+ connection.send(msg);
+ }
+ }
+
+ /**
+ * Called internally when an RpcCallMessage is received from
+ * the remote connection.
+ */
+ public void handleMessage( RpcCallMessage msg ) {
+
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "handleMessage({0})", msg);
+ }
+ RpcHandler handler = handlers.get(msg.getObjectId());
+ try {
+ if( handler == null ) {
+ throw new RuntimeException("Handler not found for objectID:" + msg.getObjectId());
+ }
+ Object result = handler.call(this, msg.getObjectId(), msg.getProcedureId(), msg.getArguments());
+ if( !msg.isAsync() ) {
+ send(msg.getChannel(), new RpcResponseMessage(msg.getMessageId(), result));
+ }
+ } catch( Exception e ) {
+ if( !msg.isAsync() ) {
+ send(msg.getChannel(), new RpcResponseMessage(msg.getMessageId(), e));
+ } else {
+ log.log(Level.SEVERE, "Error invoking async call for:" + msg, e);
+ }
+ }
+ }
+
+ /**
+ * Called internally when an RpcResponseMessage is received from
+ * the remote connection.
+ */
+ public void handleMessage( RpcResponseMessage msg ) {
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "handleMessage({0})", msg);
+ }
+ ResponseHolder holder = responses.remove(msg.getMessageId());
+ if( holder == null ) {
+ return;
+ }
+ holder.setResponse(msg);
+ }
+
+ /**
+ * Sort of like a Future, holds a locked reference to a response
+ * until the remote call has completed and returned a response.
+ */
+ private class ResponseHolder {
+ private Object response;
+ private String error;
+ private RpcCallMessage msg;
+ boolean received = false;
+
+ public ResponseHolder( RpcCallMessage msg ) {
+ this.msg = msg;
+ }
+
+ public synchronized void setResponse( RpcResponseMessage msg ) {
+ this.response = msg.getResult();
+ this.error = msg.getError();
+ this.received = true;
+ notifyAll();
+ }
+
+ public synchronized Object getResponse() {
+ try {
+ while(!received) {
+ wait();
+ }
+ } catch( InterruptedException e ) {
+ throw new RuntimeException("Interrupted waiting for respone to:" + msg, e);
+ }
+ if( error != null ) {
+ throw new RuntimeException("Error calling remote procedure:" + msg + "\n" + error);
+ }
+ return response;
+ }
+
+ public synchronized void release() {
+ if( received ) {
+ return;
+ }
+ // Else signal an error for the callers
+ this.error = "Closing connection";
+ this.received = true;
+ }
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHandler.java b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHandler.java
new file mode 100644
index 000000000..d7d99981d
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHandler.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 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.network.service.rpc;
+
+
+/**
+ * Implementations of this interface can be registered with
+ * the RpcClientService or RpcHostService to handle the
+ * remote procedure calls for a given object or objects.
+ *
+ * @author Paul Speed
+ */
+public interface RpcHandler {
+
+ /**
+ * Called when a remote procedure call request is received for a particular
+ * object from the other end of the network connection.
+ */
+ public Object call( RpcConnection conn, short objectId, short procId, Object... args );
+}
+
+
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHostedService.java b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHostedService.java
new file mode 100644
index 000000000..4a347b5a3
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHostedService.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2015 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.network.service.rpc;
+
+import com.jme3.network.HostedConnection;
+import com.jme3.network.Server;
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.util.SessionDataDelegator;
+import com.jme3.network.service.AbstractHostedService;
+import com.jme3.network.service.HostedServiceManager;
+import com.jme3.network.service.rpc.msg.RpcCallMessage;
+import com.jme3.network.service.rpc.msg.RpcResponseMessage;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * RPC service that can be added to a network Server to
+ * add RPC send/receive capabilities. For a particular
+ * HostedConnection, Remote procedure calls can be made to the
+ * associated Client and responses retrieved. Any remote procedure
+ * calls that the Client performs for this connection will be
+ * received by this service and delegated to the appropriate RpcHandlers.
+ *
+ * Note: it can be dangerous for a server to perform synchronous
+ * RPC calls to a client but especially so if not done as part
+ * of the response to some other message. ie: iterating over all
+ * or some HostedConnections to perform synchronous RPC calls
+ * will be slow and potentially block the server's threads in ways
+ * that can cause deadlocks or odd contention.
+ *
+ * @author Paul Speed
+ */
+public class RpcHostedService extends AbstractHostedService {
+
+ private static final String ATTRIBUTE_NAME = "rpcSession";
+
+ static final Logger log = Logger.getLogger(RpcHostedService.class.getName());
+
+ private boolean autoHost;
+ private SessionDataDelegator delegator;
+
+ /**
+ * Creates a new RPC host service that can be registered
+ * with the Network server and will automatically 'host'
+ * RPC services and each new network connection.
+ */
+ public RpcHostedService() {
+ this(true);
+ }
+
+ /**
+ * Creates a new RPC host service that can be registered
+ * with the Network server and will optionally 'host'
+ * RPC services and each new network connection depending
+ * on the specified 'autoHost' flag.
+ */
+ public RpcHostedService( boolean autoHost ) {
+ this.autoHost = autoHost;
+
+ // This works for me... has to be different in
+ // the general case
+ Serializer.registerClasses(RpcCallMessage.class, RpcResponseMessage.class);
+ }
+
+ /**
+ * Used internally to setup the message delegator that will
+ * handle HostedConnection specific messages and forward them
+ * to that connection's RpcConnection.
+ */
+ @Override
+ protected void onInitialize( HostedServiceManager serviceManager ) {
+ Server server = serviceManager.getServer();
+
+ // A general listener for forwarding the messages
+ // to the client-specific handler
+ this.delegator = new SessionDataDelegator(RpcConnection.class,
+ ATTRIBUTE_NAME,
+ true);
+ server.addMessageListener(delegator, delegator.getMessageTypes());
+
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "Registered delegator for message types:{0}", Arrays.asList(delegator.getMessageTypes()));
+ }
+ }
+
+ /**
+ * When set to true, all new connections will automatically have
+ * RPC hosting services attached to them, meaning they can send
+ * and receive RPC calls. If this is set to false then it is up
+ * to other services to eventually call startHostingOnConnection().
+ *
+ *
Reasons for doing this vary but usually would be because
+ * the client shouldn't be allowed to perform any RPC calls until
+ * it has provided more information. In general, this is unnecessary
+ * because the RpcHandler registries are not shared. Each client
+ * gets their own and RPC calls will fail until the appropriate
+ * objects have been registtered.
+ */
+ public void setAutoHost( boolean b ) {
+ this.autoHost = b;
+ }
+
+ /**
+ * Returns true if this service automatically attaches RPC
+ * hosting capabilities to new connections.
+ */
+ public boolean getAutoHost() {
+ return autoHost;
+ }
+
+ /**
+ * Retrieves the RpcConnection for the specified HostedConnection
+ * if that HostedConnection has had RPC services started using
+ * startHostingOnConnection() (or via autohosting). Returns null
+ * if the connection currently doesn't have RPC hosting services
+ * attached.
+ */
+ public RpcConnection getRpcConnection( HostedConnection hc ) {
+ return hc.getAttribute(ATTRIBUTE_NAME);
+ }
+
+ /**
+ * Sets up RPC hosting services for the hosted connection allowing
+ * getRpcConnection() to return a valid RPC connection object.
+ * This method is called automatically for all new connections if
+ * autohost is set to true.
+ */
+ public void startHostingOnConnection( HostedConnection hc ) {
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "startHostingOnConnection:{0}", hc);
+ }
+ hc.setAttribute(ATTRIBUTE_NAME, new RpcConnection(hc));
+ }
+
+ /**
+ * Removes any RPC hosting services associated with the specified
+ * connection. Calls to getRpcConnection() will return null for
+ * this connection. The connection's RpcConnection is also closed,
+ * releasing any waiting synchronous calls with a "Connection closing"
+ * error.
+ * This method is called automatically for all leaving connections if
+ * autohost is set to true.
+ */
+ public void stopHostingOnConnection( HostedConnection hc ) {
+ RpcConnection rpc = hc.getAttribute(ATTRIBUTE_NAME);
+ if( rpc == null ) {
+ return;
+ }
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "stopHostingOnConnection:{0}", hc);
+ }
+ hc.setAttribute(ATTRIBUTE_NAME, null);
+ rpc.close();
+ }
+
+ /**
+ * Used internally to remove the message delegator from the
+ * server.
+ */
+ @Override
+ public void terminate(HostedServiceManager serviceManager) {
+ Server server = serviceManager.getServer();
+ server.removeMessageListener(delegator, delegator.getMessageTypes());
+ }
+
+ /**
+ * Called internally when a new connection is detected for
+ * the server. If the current autoHost property is true then
+ * startHostingOnConnection(hc) is called.
+ */
+ @Override
+ public void connectionAdded(Server server, HostedConnection hc) {
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "connectionAdded({0}, {1})", new Object[]{server, hc});
+ }
+ if( autoHost ) {
+ startHostingOnConnection(hc);
+ }
+ }
+
+ /**
+ * Called internally when an existing connection is leaving
+ * the server. If the current autoHost property is true then
+ * stopHostingOnConnection(hc) is called.
+ */
+ @Override
+ public void connectionRemoved(Server server, HostedConnection hc) {
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "connectionRemoved({0}, {1})", new Object[]{server, hc});
+ }
+ stopHostingOnConnection(hc);
+ }
+
+}
+
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcCallMessage.java b/jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcCallMessage.java
new file mode 100644
index 000000000..70f12f1e0
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcCallMessage.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015 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.network.service.rpc.msg;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+
+
+/**
+ * Used internally to send RPC call information to
+ * the other end of a connection for execution.
+ *
+ * @author Paul Speed
+ */
+@Serializable
+public class RpcCallMessage extends AbstractMessage {
+
+ private long msgId;
+ private byte channel;
+ private short objId;
+ private short procId;
+ private Object[] args;
+
+ public RpcCallMessage() {
+ }
+
+ public RpcCallMessage( long msgId, byte channel, short objId, short procId, Object... args ) {
+ this.msgId = msgId;
+ this.channel = channel;
+ this.objId = objId;
+ this.procId = procId;
+ this.args = args;
+ }
+
+ public long getMessageId() {
+ return msgId;
+ }
+
+ public byte getChannel() {
+ return channel;
+ }
+
+ public boolean isAsync() {
+ return msgId == -1;
+ }
+
+ public short getObjectId() {
+ return objId;
+ }
+
+ public short getProcedureId() {
+ return procId;
+ }
+
+ public Object[] getArguments() {
+ return args;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[#" + msgId + ", channel=" + channel
+ + (isAsync() ? ", async" : ", sync")
+ + ", objId=" + objId
+ + ", procId=" + procId
+ + ", args.length=" + args.length
+ + "]";
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcResponseMessage.java b/jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcResponseMessage.java
new file mode 100644
index 000000000..efb0def6a
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcResponseMessage.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2015 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.network.service.rpc.msg;
+
+import com.jme3.network.AbstractMessage;
+import com.jme3.network.serializing.Serializable;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * Used internally to send an RPC call's response back to
+ * the caller.
+ *
+ * @author Paul Speed
+ */
+@Serializable
+public class RpcResponseMessage extends AbstractMessage {
+
+ private long msgId;
+ private Object result;
+ private String error;
+
+ public RpcResponseMessage() {
+ }
+
+ public RpcResponseMessage( long msgId, Object result ) {
+ this.msgId = msgId;
+ this.result = result;
+ }
+
+ public RpcResponseMessage( long msgId, Throwable t ) {
+ this.msgId = msgId;
+
+ StringWriter sOut = new StringWriter();
+ PrintWriter out = new PrintWriter(sOut);
+ t.printStackTrace(out);
+ out.close();
+ this.error = sOut.toString();
+ }
+
+ public long getMessageId() {
+ return msgId;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[#" + msgId + ", result=" + result
+ + "]";
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/serializer/ClientSerializerRegistrationsService.java b/jme3-networking/src/main/java/com/jme3/network/service/serializer/ClientSerializerRegistrationsService.java
new file mode 100644
index 000000000..911ce0fb1
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/serializer/ClientSerializerRegistrationsService.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 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.network.service.serializer;
+
+import com.jme3.network.Client;
+import com.jme3.network.Message;
+import com.jme3.network.MessageListener;
+import com.jme3.network.message.SerializerRegistrationsMessage;
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.service.AbstractClientService;
+import com.jme3.network.service.ClientServiceManager;
+
+
+/**
+ *
+ *
+ * @author Paul Speed
+ */
+public class ClientSerializerRegistrationsService extends AbstractClientService
+ implements MessageListener {
+
+ @Override
+ protected void onInitialize( ClientServiceManager serviceManager ) {
+ // Make sure our message type is registered
+ // This is the minimum we'd need just to be able to register
+ // the rest... otherwise we can't even receive this message.
+ Serializer.registerClass(SerializerRegistrationsMessage.class);
+ Serializer.registerClass(SerializerRegistrationsMessage.Registration.class);
+
+ // Add our listener for that message type
+ serviceManager.getClient().addMessageListener(this, SerializerRegistrationsMessage.class);
+ }
+
+ public void messageReceived( Client source, Message m ) {
+ // We only wait for one kind of message...
+ SerializerRegistrationsMessage msg = (SerializerRegistrationsMessage)m;
+ msg.registerAll();
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/service/serializer/ServerSerializerRegistrationsService.java b/jme3-networking/src/main/java/com/jme3/network/service/serializer/ServerSerializerRegistrationsService.java
new file mode 100644
index 000000000..b24a8db5f
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/service/serializer/ServerSerializerRegistrationsService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015 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.network.service.serializer;
+
+import com.jme3.network.HostedConnection;
+import com.jme3.network.Server;
+import com.jme3.network.message.SerializerRegistrationsMessage;
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.service.AbstractHostedService;
+import com.jme3.network.service.HostedServiceManager;
+
+
+/**
+ *
+ *
+ * @author Paul Speed
+ */
+public class ServerSerializerRegistrationsService extends AbstractHostedService {
+
+ @Override
+ protected void onInitialize( HostedServiceManager serviceManager ) {
+ // Make sure our message type is registered
+ Serializer.registerClass(SerializerRegistrationsMessage.class);
+ Serializer.registerClass(SerializerRegistrationsMessage.Registration.class);
+ }
+
+ @Override
+ public void start() {
+ // Compile the registrations into a message we will
+ // send to all connecting clients
+ SerializerRegistrationsMessage.compile();
+ }
+
+ @Override
+ public void connectionAdded(Server server, HostedConnection hc) {
+ // Just in case
+ super.connectionAdded(server, hc);
+
+ // Send the client the registration information
+ hc.send(SerializerRegistrationsMessage.INSTANCE);
+ }
+}
diff --git a/jme3-networking/src/main/java/com/jme3/network/util/AbstractMessageDelegator.java b/jme3-networking/src/main/java/com/jme3/network/util/AbstractMessageDelegator.java
new file mode 100644
index 000000000..a73496ea8
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/util/AbstractMessageDelegator.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2015 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.network.util;
+
+import com.jme3.network.Message;
+import com.jme3.network.MessageConnection;
+import com.jme3.network.MessageListener;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * A MessageListener implementation that will forward messages to methods
+ * of a delegate object. These methods can be automapped or manually
+ * specified. Subclasses provide specific implementations for how to
+ * find the actual delegate object.
+ *
+ * @author Paul Speed
+ */
+public abstract class AbstractMessageDelegator
+ implements MessageListener {
+
+ static final Logger log = Logger.getLogger(AbstractMessageDelegator.class.getName());
+
+ private Class delegateType;
+ private Map methods = new HashMap();
+ private Class[] messageTypes;
+
+ /**
+ * Creates an AbstractMessageDelegator that will forward received
+ * messages to methods of the specified delegate type. If automap
+ * is true then reflection is used to lookup probably message handling
+ * methods.
+ */
+ protected AbstractMessageDelegator( Class delegateType, boolean automap ) {
+ this.delegateType = delegateType;
+ if( automap ) {
+ automap();
+ }
+ }
+
+ /**
+ * Returns the array of messages known to be handled by this message
+ * delegator.
+ */
+ public Class[] getMessageTypes() {
+ if( messageTypes == null ) {
+ messageTypes = methods.keySet().toArray(new Class[methods.size()]);
+ }
+ return messageTypes;
+ }
+
+ /**
+ * Returns true if the specified method is valid for the specified
+ * message type. This is used internally during automapping to
+ * provide implementation specific filting of methods.
+ * This implementation checks for methods that take either the connection and message
+ * type arguments (in that order) or just the message type.
+ */
+ protected boolean isValidMethod( Method m, Class messageType ) {
+
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "isValidMethod({0}, {1})", new Object[]{m, messageType});
+ }
+
+ // Parameters must be S and message type or just message type
+ Class>[] parms = m.getParameterTypes();
+ if( parms.length != 2 && parms.length != 1 ) {
+ log.finest("Parameter count is not 1 or 2");
+ return false;
+ }
+ int messageIndex = 0;
+ if( parms.length > 1 ) {
+ if( MessageConnection.class.isAssignableFrom(parms[0]) ) {
+ messageIndex++;
+ } else {
+ log.finest("First paramter is not a MessageConnection or subclass.");
+ return false;
+ }
+ }
+
+ if( messageType == null && !Message.class.isAssignableFrom(parms[messageIndex]) ) {
+ log.finest("Second paramter is not a Message or subclass.");
+ return false;
+ }
+ if( messageType != null && !parms[messageIndex].isAssignableFrom(messageType) ) {
+ log.log(Level.FINEST, "Second paramter is not a {0}", messageType);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Convenience method that returns the message type as
+ * reflecively determined for a particular method. This
+ * only works with methods that actually have arguments.
+ * This implementation returns the last element of the method's
+ * getParameterTypes() array, thus supporting both
+ * method(connection, messageType) as well as just method(messageType)
+ * calling forms.
+ */
+ protected Class getMessageType( Method m ) {
+ Class>[] parms = m.getParameterTypes();
+ return parms[parms.length-1];
+ }
+
+ /**
+ * Goes through all of the delegate type's methods to find
+ * a method of the specified name that may take the specified
+ * message type.
+ */
+ protected Method findDelegate( String name, Class messageType ) {
+ // We do an exhaustive search because it's easier to
+ // check for a variety of parameter types and it's all
+ // that Class would be doing in getMethod() anyway.
+ for( Method m : delegateType.getDeclaredMethods() ) {
+
+ if( !m.getName().equals(name) ) {
+ continue;
+ }
+
+ if( isValidMethod(m, messageType) ) {
+ return m;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns true if the specified method name is allowed.
+ * This is used by automapping to determine if a method
+ * should be rejected purely on name. Default implemention
+ * always returns true.
+ */
+ protected boolean allowName( String name ) {
+ return true;
+ }
+
+ /**
+ * Calls the map(Set) method with a null argument causing
+ * all available matching methods to mapped to message types.
+ */
+ protected final void automap() {
+ map((Set)null);
+ if( methods.isEmpty() ) {
+ throw new RuntimeException("No message handling methods found for class:" + delegateType);
+ }
+ }
+
+ /**
+ * Specifically maps the specified methods names, autowiring
+ * the parameters.
+ */
+ public AbstractMessageDelegator map( String... methodNames ) {
+ Set names = new HashSet( Arrays.asList(methodNames) );
+ map(names);
+ return this;
+ }
+
+ /**
+ * Goes through all of the delegate type's declared methods
+ * mapping methods that match the current constraints.
+ * If the constraints set is null then allowName() is
+ * checked for names otherwise only names in the constraints
+ * set are allowed.
+ * For each candidate method that passes the above checks,
+ * isValidMethod() is called with a null message type argument.
+ * All methods are made accessible thus supporting non-public
+ * methods as well as public methods.
+ */
+ protected void map( Set constraints ) {
+
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "map({0})", constraints);
+ }
+ for( Method m : delegateType.getDeclaredMethods() ) {
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "Checking method:{0}", m);
+ }
+
+ if( constraints == null && !allowName(m.getName()) ) {
+ log.finest("Name is not allowed.");
+ continue;
+ }
+ if( constraints != null && !constraints.contains(m.getName()) ) {
+ log.finest("Name is not in constraints set.");
+ continue;
+ }
+
+ if( isValidMethod(m, null) ) {
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "Adding method mapping:{0} = {1}", new Object[]{getMessageType(m), m});
+ }
+ // Make sure we can access the method even if it's not public or
+ // is in a non-public inner class.
+ m.setAccessible(true);
+ methods.put(getMessageType(m), m);
+ }
+ }
+
+ messageTypes = null;
+ }
+
+ /**
+ * Manually maps a specified method to the specified message type.
+ */
+ public AbstractMessageDelegator map( Class messageType, String methodName ) {
+ // Lookup the method
+ Method m = findDelegate( methodName, messageType );
+ if( m == null ) {
+ throw new RuntimeException( "Method:" + methodName
+ + " not found matching signature (MessageConnection, "
+ + messageType.getName() + ")" );
+ }
+
+ if( log.isLoggable(Level.FINEST) ) {
+ log.log(Level.FINEST, "Adding method mapping:{0} = {1}", new Object[]{messageType, m});
+ }
+ methods.put( messageType, m );
+ messageTypes = null;
+ return this;
+ }
+
+ /**
+ * Returns the mapped method for the specified message type.
+ */
+ protected Method getMethod( Class c ) {
+ Method m = methods.get(c);
+ return m;
+ }
+
+ /**
+ * Implemented by subclasses to provide the actual delegate object
+ * against which the mapped message type methods will be called.
+ */
+ protected abstract Object getSourceDelegate( S source );
+
+ /**
+ * Implementation of the MessageListener's messageReceived()
+ * method that will use the current message type mapping to
+ * find an appropriate message handling method and call it
+ * on the delegate returned by getSourceDelegate().
+ */
+ @Override
+ public void messageReceived( S source, Message msg ) {
+ if( msg == null ) {
+ return;
+ }
+
+ Object delegate = getSourceDelegate(source);
+ if( delegate == null ) {
+ // Means ignore this message/source
+ return;
+ }
+
+ Method m = getMethod(msg.getClass());
+ if( m == null ) {
+ throw new RuntimeException("Delegate method not found for message class:"
+ + msg.getClass());
+ }
+
+ try {
+ if( m.getParameterTypes().length > 1 ) {
+ m.invoke( delegate, source, msg );
+ } else {
+ m.invoke( delegate, msg );
+ }
+ } catch( IllegalAccessException e ) {
+ throw new RuntimeException("Error executing:" + m, e);
+ } catch( InvocationTargetException e ) {
+ throw new RuntimeException("Error executing:" + m, e.getCause());
+ }
+ }
+}
+
+
diff --git a/jme3-networking/src/main/java/com/jme3/network/util/ObjectMessageDelegator.java b/jme3-networking/src/main/java/com/jme3/network/util/ObjectMessageDelegator.java
new file mode 100644
index 000000000..b92ee09a8
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/util/ObjectMessageDelegator.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015 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.network.util;
+
+import com.jme3.network.MessageConnection;
+
+
+/**
+ * A MessageListener implementation that will forward messages to methods
+ * of a specified delegate object. These methods can be automapped or manually
+ * specified.
+ *
+ * @author Paul Speed
+ */
+public class ObjectMessageDelegator extends AbstractMessageDelegator {
+
+ private Object delegate;
+
+ /**
+ * Creates a MessageListener that will forward mapped message types
+ * to methods of the specified object.
+ * If automap is true then all methods with the proper signature will
+ * be mapped.
+ *
Methods of the following signatures are allowed:
+ *
+ *
void someName(S conn, SomeMessage msg)
+ *
void someName(Message msg)
+ *
+ * Where S is the type of MessageConnection and SomeMessage is some
+ * specific concreate Message subclass.
+ */
+ public ObjectMessageDelegator( Object delegate, boolean automap ) {
+ super(delegate.getClass(), automap);
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected Object getSourceDelegate( MessageConnection source ) {
+ return delegate;
+ }
+}
+
diff --git a/jme3-networking/src/main/java/com/jme3/network/util/SessionDataDelegator.java b/jme3-networking/src/main/java/com/jme3/network/util/SessionDataDelegator.java
new file mode 100644
index 000000000..f2bee3373
--- /dev/null
+++ b/jme3-networking/src/main/java/com/jme3/network/util/SessionDataDelegator.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015 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.network.util;
+
+import com.jme3.network.HostedConnection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * A MessageListener implementation that will forward messages to methods
+ * of a delegate specified as a HostedConnection session attribute. This is
+ * useful for handling connection-specific messages from clients that must
+ * delegate to client-specific data objects.
+ * The delegate methods can be automapped or manually specified.
+ *
+ * @author Paul Speed
+ */
+public class SessionDataDelegator extends AbstractMessageDelegator {
+
+ static final Logger log = Logger.getLogger(SessionDataDelegator.class.getName());
+
+ private String attributeName;
+
+ /**
+ * Creates a MessageListener that will forward mapped message types
+ * to methods of an object specified as a HostedConnection attribute.
+ * If automap is true then all methods with the proper signature will
+ * be mapped.
+ *
Methods of the following signatures are allowed:
+ *
+ *
void someName(S conn, SomeMessage msg)
+ *
void someName(Message msg)
+ *
+ * Where S is the type of MessageConnection and SomeMessage is some
+ * specific concreate Message subclass.
+ */
+ public SessionDataDelegator( Class delegateType, String attributeName, boolean automap ) {
+ super(delegateType, automap);
+ this.attributeName = attributeName;
+ }
+
+ /**
+ * Returns the attribute name that will be used to look up the
+ * delegate object.
+ */
+ public String getAttributeName() {
+ return attributeName;
+ }
+
+ /**
+ * Called internally when there is no session object
+ * for the current attribute name attached to the passed source
+ * HostConnection. Default implementation logs a warning.
+ */
+ protected void miss( HostedConnection source ) {
+ log.log(Level.WARNING, "Session data is null for:{0} on connection:{1}", new Object[]{attributeName, source});
+ }
+
+ /**
+ * Returns the attributeName attribute of the supplied source
+ * HostConnection. If there is no value at that attribute then
+ * the miss() method is called.
+ */
+ protected Object getSourceDelegate( HostedConnection source ) {
+ Object result = source.getAttribute(attributeName);
+ if( result == null ) {
+ miss(source);
+ }
+ return result;
+ }
+}
+
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/ContentTextureKey.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/ContentTextureKey.java
index 06a4ec795..35375f9c5 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/ContentTextureKey.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/ContentTextureKey.java
@@ -32,6 +32,8 @@
package com.jme3.scene.plugins.fbx;
import com.jme3.asset.TextureKey;
+import com.jme3.asset.cache.AssetCache;
+import com.jme3.asset.cache.WeakRefCloneAssetCache;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
@@ -56,6 +58,13 @@ public class ContentTextureKey extends TextureKey {
return content;
}
+ @Override
+ public Class extends AssetCache> getCacheType(){
+ // Need to override this so that textures embedded in FBX
+ // don't get cached by the asset manager.
+ return null;
+ }
+
@Override
public String toString() {
return super.toString() + " " + content.length + " bytes";
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/FbxLoader.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/FbxLoader.java
new file mode 100644
index 000000000..fd157cb48
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/FbxLoader.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2009-2015 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.scene.plugins.fbx;
+
+import com.jme3.animation.AnimControl;
+import com.jme3.animation.Animation;
+import com.jme3.animation.Bone;
+import com.jme3.animation.BoneTrack;
+import com.jme3.animation.Skeleton;
+import com.jme3.animation.Track;
+import com.jme3.asset.AssetInfo;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetLoadException;
+import com.jme3.asset.AssetLoader;
+import com.jme3.asset.AssetManager;
+import com.jme3.asset.ModelKey;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Transform;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.fbx.anim.FbxToJmeTrack;
+import com.jme3.scene.plugins.fbx.anim.FbxAnimCurveNode;
+import com.jme3.scene.plugins.fbx.anim.FbxAnimLayer;
+import com.jme3.scene.plugins.fbx.anim.FbxAnimStack;
+import com.jme3.scene.plugins.fbx.anim.FbxBindPose;
+import com.jme3.scene.plugins.fbx.anim.FbxLimbNode;
+import com.jme3.scene.plugins.fbx.file.FbxDump;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.plugins.fbx.file.FbxFile;
+import com.jme3.scene.plugins.fbx.file.FbxReader;
+import com.jme3.scene.plugins.fbx.file.FbxId;
+import com.jme3.scene.plugins.fbx.misc.FbxGlobalSettings;
+import com.jme3.scene.plugins.fbx.node.FbxNode;
+import com.jme3.scene.plugins.fbx.node.FbxRootNode;
+import com.jme3.scene.plugins.fbx.obj.FbxObjectFactory;
+import com.jme3.scene.plugins.fbx.obj.FbxObject;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FbxLoader implements AssetLoader {
+
+ private static final Logger logger = Logger.getLogger(FbxLoader.class.getName());
+
+ private AssetManager assetManager;
+
+ private String sceneName;
+ private String sceneFilename;
+ private String sceneFolderName;
+ private FbxGlobalSettings globalSettings;
+ private final Map objectMap = new HashMap();
+
+ private final List animStacks = new ArrayList();
+ private final List bindPoses = new ArrayList();
+
+ @Override
+ public Object load(AssetInfo assetInfo) throws IOException {
+ this.assetManager = assetInfo.getManager();
+ AssetKey> assetKey = assetInfo.getKey();
+ if (!(assetKey instanceof ModelKey)) {
+ throw new AssetLoadException("Invalid asset key");
+ }
+
+ InputStream stream = assetInfo.openStream();
+ try {
+ sceneFilename = assetKey.getName();
+ sceneFolderName = assetKey.getFolder();
+ String ext = assetKey.getExtension();
+
+ sceneName = sceneFilename.substring(0, sceneFilename.length() - ext.length() - 1);
+ if (sceneFolderName != null && sceneFolderName.length() > 0) {
+ sceneName = sceneName.substring(sceneFolderName.length());
+ }
+
+ reset();
+
+ // Load the data from the stream.
+ loadData(stream);
+
+ // Bind poses are needed to compute world transforms.
+ applyBindPoses();
+
+ // Need world transforms for skeleton creation.
+ updateWorldTransforms();
+
+ // Need skeletons for meshs to be created in scene graph construction.
+ // Mesh bone indices require skeletons to determine bone index.
+ constructSkeletons();
+
+ // Create the jME3 scene graph from the FBX scene graph.
+ // Also creates SkeletonControls based on the constructed skeletons.
+ Spatial scene = constructSceneGraph();
+
+ // Load animations into AnimControls
+ constructAnimations();
+
+ return scene;
+ } finally {
+ releaseObjects();
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+
+ private void reset() {
+ globalSettings = new FbxGlobalSettings();
+ }
+
+ private void releaseObjects() {
+ globalSettings = null;
+ objectMap.clear();
+ animStacks.clear();
+ }
+
+ private void loadData(InputStream stream) throws IOException {
+ FbxFile scene = FbxReader.readFBX(stream);
+
+ FbxDump.dumpFile(scene);
+
+ // TODO: Load FBX object templates
+
+ for (FbxElement e : scene.rootElements) {
+ if (e.id.equals("FBXHeaderExtension")) {
+ loadHeader(e);
+ } else if (e.id.equals("GlobalSettings")) {
+ loadGlobalSettings(e);
+ } else if (e.id.equals("Objects")) {
+ loadObjects(e);
+ } else if (e.id.equals("Connections")) {
+ connectObjects(e);
+ }
+ }
+ }
+
+ private void loadHeader(FbxElement element) {
+ for (FbxElement e : element.children) {
+ if (e.id.equals("FBXVersion")) {
+ Integer version = (Integer) e.properties.get(0);
+ if (version < 7100) {
+ logger.log(Level.WARNING, "FBX file version is older than 7.1. "
+ + "Some features may not work.");
+ }
+ }
+ }
+ }
+
+ private void loadGlobalSettings(FbxElement element) {
+ globalSettings = new FbxGlobalSettings();
+ globalSettings.fromElement(element);
+ }
+
+ private void loadObjects(FbxElement element) {
+ // Initialize the FBX root element.
+ objectMap.put(FbxId.ROOT, new FbxRootNode(assetManager, sceneFolderName));
+
+ for(FbxElement e : element.children) {
+ if (e.id.equals("GlobalSettings")) {
+ // Old FBX files seem to have the GlobalSettings element
+ // under Objects (??)
+ globalSettings.fromElement(e);
+ } else {
+ FbxObject object = FbxObjectFactory.createObject(e, assetManager, sceneFolderName);
+ if (object != null) {
+ if (objectMap.containsKey(object.getId())) {
+ logger.log(Level.WARNING, "An object with ID \"{0}\" has "
+ + "already been defined. "
+ + "Ignoring.",
+ object.getId());
+ }
+
+ objectMap.put(object.getId(), object);
+
+ if (object instanceof FbxAnimStack) {
+ // NOTE: animation stacks are implicitly global.
+ // Capture them here.
+ animStacks.add((FbxAnimStack) object);
+ } else if (object instanceof FbxBindPose) {
+ bindPoses.add((FbxBindPose) object);
+ }
+ } else {
+ throw new UnsupportedOperationException("Failed to create FBX object of type: " + e.id);
+ }
+ }
+ }
+ }
+
+ private void removeUnconnectedObjects() {
+ for (FbxObject object : new ArrayList(objectMap.values())) {
+ if (!object.isJmeObjectCreated()) {
+ logger.log(Level.WARNING, "Purging orphan FBX object: {0}", object);
+ objectMap.remove(object.getId());
+ }
+ }
+ }
+
+ private void connectObjects(FbxElement element) {
+ if (objectMap.isEmpty()) {
+ logger.log(Level.WARNING, "FBX file is missing object information");
+ return;
+ } else if (objectMap.size() == 1) {
+ // Only root node (automatically added by jME3)
+ logger.log(Level.WARNING, "FBX file has no objects");
+ return;
+ }
+
+ for (FbxElement el : element.children) {
+ if (!el.id.equals("C") && !el.id.equals("Connect")) {
+ continue;
+ }
+ String type = (String) el.properties.get(0);
+ FbxId childId;
+ FbxId parentId;
+ if (type.equals("OO")) {
+ childId = FbxId.create(el.properties.get(1));
+ parentId = FbxId.create(el.properties.get(2));
+ FbxObject child = objectMap.get(childId);
+ FbxObject parent;
+
+ if (parentId.isNull()) {
+ // TODO: maybe clean this up a bit..
+ parent = objectMap.get(FbxId.ROOT);
+ } else {
+ parent = objectMap.get(parentId);
+ }
+
+ if (parent == null) {
+ throw new UnsupportedOperationException("Cannot find parent object ID \"" + parentId + "\"");
+ }
+
+ parent.connectObject(child);
+ } else if (type.equals("OP")) {
+ childId = FbxId.create(el.properties.get(1));
+ parentId = FbxId.create(el.properties.get(2));
+ String propName = (String) el.properties.get(3);
+ FbxObject child = objectMap.get(childId);
+ FbxObject parent = objectMap.get(parentId);
+ parent.connectObjectProperty(child, propName);
+ } else {
+ logger.log(Level.WARNING, "Unknown connection type: {0}. Ignoring.", type);
+ }
+ }
+ }
+
+ /**
+ * Copies the bind poses from FBX BindPose objects to FBX nodes.
+ * Must be called prior to {@link #updateWorldTransforms()}.
+ */
+ private void applyBindPoses() {
+ for (FbxBindPose bindPose : bindPoses) {
+ Map bindPoseData = bindPose.getJmeObject();
+ logger.log(Level.INFO, "Applying {0} bind poses", bindPoseData.size());
+ for (Map.Entry entry : bindPoseData.entrySet()) {
+ FbxObject obj = objectMap.get(entry.getKey());
+ if (obj instanceof FbxNode) {
+ FbxNode node = (FbxNode) obj;
+ node.setWorldBindPose(entry.getValue());
+ } else {
+ logger.log(Level.WARNING, "Bind pose can only be applied to FBX nodes. Ignoring.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates world transforms and bind poses for the FBX scene graph.
+ */
+ private void updateWorldTransforms() {
+ FbxNode fbxRoot = (FbxNode) objectMap.get(FbxId.ROOT);
+ fbxRoot.updateWorldTransforms(null, null);
+ }
+
+ private void constructAnimations() {
+ // In FBX, animation are not attached to any root.
+ // They are implicitly global.
+ // So, we need to use hueristics to find which node(s)
+ // an animation is associated with, so we can create the AnimControl
+ // in the appropriate location in the scene.
+ Map pairs = new HashMap();
+ for (FbxAnimStack stack : animStacks) {
+ for (FbxAnimLayer layer : stack.getLayers()) {
+ for (FbxAnimCurveNode curveNode : layer.getAnimationCurveNodes()) {
+ for (Map.Entry nodePropertyEntry : curveNode.getInfluencedNodeProperties().entrySet()) {
+ FbxToJmeTrack lookupPair = new FbxToJmeTrack();
+ lookupPair.animStack = stack;
+ lookupPair.animLayer = layer;
+ lookupPair.node = nodePropertyEntry.getKey();
+
+ // Find if this pair is already stored
+ FbxToJmeTrack storedPair = pairs.get(lookupPair);
+ if (storedPair == null) {
+ // If not, store it.
+ storedPair = lookupPair;
+ pairs.put(storedPair, storedPair);
+ }
+
+ String property = nodePropertyEntry.getValue();
+ storedPair.animCurves.put(property, curveNode);
+ }
+ }
+ }
+ }
+
+ // At this point we can construct the animation for all pairs ...
+ for (FbxToJmeTrack pair : pairs.values()) {
+ String animName = pair.animStack.getName();
+ float duration = pair.animStack.getDuration();
+
+ System.out.println("ANIMATION: " + animName + ", duration = " + duration);
+ System.out.println("NODE: " + pair.node.getName());
+
+ duration = pair.getDuration();
+
+ if (pair.node instanceof FbxLimbNode) {
+ // Find the spatial that has the skeleton for this limb.
+ FbxLimbNode limbNode = (FbxLimbNode) pair.node;
+ Bone bone = limbNode.getJmeBone();
+ Spatial jmeSpatial = limbNode.getSkeletonHolder().getJmeObject();
+ Skeleton skeleton = limbNode.getSkeletonHolder().getJmeSkeleton();
+
+ // Get the animation control (create if missing).
+ AnimControl animControl = jmeSpatial.getControl(AnimControl.class);
+ if (animControl.getSkeleton() != skeleton) {
+ throw new UnsupportedOperationException();
+ }
+
+ // Get the animation (create if missing).
+ Animation anim = animControl.getAnim(animName);
+ if (anim == null) {
+ anim = new Animation(animName, duration);
+ animControl.addAnim(anim);
+ }
+
+ // Find the bone index from the spatial's skeleton.
+ int boneIndex = skeleton.getBoneIndex(bone);
+
+ // Generate the bone track.
+ BoneTrack bt = pair.toJmeBoneTrack(boneIndex, bone.getBindInverseTransform());
+
+ // Add the bone track to the animation.
+ anim.addTrack(bt);
+ } else {
+ // Create the spatial animation
+ Animation anim = new Animation(animName, duration);
+ anim.setTracks(new Track[]{ pair.toJmeSpatialTrack() });
+
+ // Get the animation control (create if missing).
+ Spatial jmeSpatial = pair.node.getJmeObject();
+ AnimControl animControl = jmeSpatial.getControl(AnimControl.class);
+
+ if (animControl == null) {
+ animControl = new AnimControl(null);
+ jmeSpatial.addControl(animControl);
+ }
+
+ // Add the spatial animation
+ animControl.addAnim(anim);
+ }
+ }
+ }
+
+ private void constructSkeletons() {
+ FbxNode fbxRoot = (FbxNode) objectMap.get(FbxId.ROOT);
+ FbxNode.createSkeletons(fbxRoot);
+ }
+
+ private Spatial constructSceneGraph() {
+ // Acquire the implicit root object.
+ FbxNode fbxRoot = (FbxNode) objectMap.get(FbxId.ROOT);
+
+ // Convert it into a jME3 scene
+ Node jmeRoot = (Node) FbxNode.createScene(fbxRoot);
+
+ // Fix the name (will probably be set to something like "-node")
+ jmeRoot.setName(sceneName + "-scene");
+
+ return jmeRoot;
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
index e767763e1..491301c22 100644
--- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java
@@ -73,9 +73,9 @@ import com.jme3.scene.Mesh.Mode;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.plugins.fbx.AnimationList.AnimInverval;
-import com.jme3.scene.plugins.fbx.file.FBXElement;
-import com.jme3.scene.plugins.fbx.file.FBXFile;
-import com.jme3.scene.plugins.fbx.file.FBXReader;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.plugins.fbx.file.FbxFile;
+import com.jme3.scene.plugins.fbx.file.FbxReader;
import com.jme3.texture.Image;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
@@ -165,8 +165,8 @@ public class SceneLoader implements AssetLoader {
private void loadScene(InputStream stream) throws IOException {
logger.log(Level.FINE, "Loading scene {0}", sceneFilename);
long startTime = System.currentTimeMillis();
- FBXFile scene = FBXReader.readFBX(stream);
- for(FBXElement e : scene.rootElements) {
+ FbxFile scene = FbxReader.readFBX(stream);
+ for(FbxElement e : scene.rootElements) {
if(e.id.equals("GlobalSettings"))
loadGlobalSettings(e);
else if(e.id.equals("Objects"))
@@ -178,10 +178,10 @@ public class SceneLoader implements AssetLoader {
logger.log(Level.FINE, "Loading done in {0} ms", estimatedTime);
}
- private void loadGlobalSettings(FBXElement element) {
- for(FBXElement e : element.children) {
+ private void loadGlobalSettings(FbxElement element) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Properties70")) {
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("P")) {
String propName = (String) e2.properties.get(0);
if(propName.equals("UnitScaleFactor"))
@@ -197,8 +197,8 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadObjects(FBXElement element) {
- for(FBXElement e : element.children) {
+ private void loadObjects(FbxElement element) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Geometry"))
loadGeometry(e);
else if(e.id.equals("Material"))
@@ -222,12 +222,12 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadGeometry(FBXElement element) {
+ private void loadGeometry(FbxElement element) {
long id = (Long) element.properties.get(0);
String type = (String) element.properties.get(2);
if(type.equals("Mesh")) {
MeshData data = new MeshData();
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Vertices"))
data.vertices = (double[]) e.properties.get(0);
else if(e.id.equals("PolygonVertexIndex"))
@@ -236,7 +236,7 @@ public class SceneLoader implements AssetLoader {
//else if(e.id.equals("Edges"))
// data.edges = (int[]) e.properties.get(0);
else if(e.id.equals("LayerElementNormal"))
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("MappingInformationType")) {
data.normalsMapping = (String) e2.properties.get(0);
if(!data.normalsMapping.equals("ByVertice") && !data.normalsMapping.equals("ByPolygonVertex"))
@@ -249,7 +249,7 @@ public class SceneLoader implements AssetLoader {
data.normals = (double[]) e2.properties.get(0);
}
else if(e.id.equals("LayerElementTangent"))
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("MappingInformationType")) {
data.tangentsMapping = (String) e2.properties.get(0);
if(!data.tangentsMapping.equals("ByVertice") && !data.tangentsMapping.equals("ByPolygonVertex"))
@@ -262,7 +262,7 @@ public class SceneLoader implements AssetLoader {
data.tangents = (double[]) e2.properties.get(0);
}
else if(e.id.equals("LayerElementBinormal"))
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("MappingInformationType")) {
data.binormalsMapping = (String) e2.properties.get(0);
if(!data.binormalsMapping.equals("ByVertice") && !data.binormalsMapping.equals("ByPolygonVertex"))
@@ -275,7 +275,7 @@ public class SceneLoader implements AssetLoader {
data.binormals = (double[]) e2.properties.get(0);
}
else if(e.id.equals("LayerElementUV"))
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("MappingInformationType")) {
data.uvMapping = (String) e2.properties.get(0);
if(!data.uvMapping.equals("ByPolygonVertex"))
@@ -291,7 +291,7 @@ public class SceneLoader implements AssetLoader {
}
// TODO smoothing is not used now
//else if(e.id.equals("LayerElementSmoothing"))
- // for(FBXElement e2 : e.children) {
+ // for(FbxElement e2 : e.children) {
// if(e2.id.equals("MappingInformationType")) {
// data.smoothingMapping = (String) e2.properties.get(0);
// if(!data.smoothingMapping.equals("ByEdge"))
@@ -304,7 +304,7 @@ public class SceneLoader implements AssetLoader {
// data.smoothing = (int[]) e2.properties.get(0);
// }
else if(e.id.equals("LayerElementMaterial"))
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("MappingInformationType")) {
data.materialsMapping = (String) e2.properties.get(0);
if(!data.materialsMapping.equals("AllSame"))
@@ -321,18 +321,18 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadMaterial(FBXElement element) {
+ private void loadMaterial(FbxElement element) {
long id = (Long) element.properties.get(0);
String path = (String) element.properties.get(1);
String type = (String) element.properties.get(2);
if(type.equals("")) {
MaterialData data = new MaterialData();
data.name = path.substring(0, path.indexOf(0));
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("ShadingModel")) {
data.shadingModel = (String) e.properties.get(0);
} else if(e.id.equals("Properties70")) {
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("P")) {
String propName = (String) e2.properties.get(0);
if(propName.equals("AmbientColor")) {
@@ -368,16 +368,16 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadModel(FBXElement element) {
+ private void loadModel(FbxElement element) {
long id = (Long) element.properties.get(0);
String path = (String) element.properties.get(1);
String type = (String) element.properties.get(2);
ModelData data = new ModelData();
data.name = path.substring(0, path.indexOf(0));
data.type = type;
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Properties70")) {
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("P")) {
String propName = (String) e2.properties.get(0);
if(propName.equals("Lcl Translation")) {
@@ -408,17 +408,17 @@ public class SceneLoader implements AssetLoader {
modelDataMap.put(id, data);
}
- private void loadPose(FBXElement element) {
+ private void loadPose(FbxElement element) {
long id = (Long) element.properties.get(0);
String path = (String) element.properties.get(1);
String type = (String) element.properties.get(2);
if(type.equals("BindPose")) {
BindPoseData data = new BindPoseData();
data.name = path.substring(0, path.indexOf(0));
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("PoseNode")) {
NodeTransformData item = new NodeTransformData();
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("Node"))
item.nodeId = (Long) e2.properties.get(0);
else if(e2.id.equals("Matrix"))
@@ -431,14 +431,14 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadTexture(FBXElement element) {
+ private void loadTexture(FbxElement element) {
long id = (Long) element.properties.get(0);
String path = (String) element.properties.get(1);
String type = (String) element.properties.get(2);
if(type.equals("")) {
TextureData data = new TextureData();
data.name = path.substring(0, path.indexOf(0));
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Type"))
data.bindType = (String) e.properties.get(0);
else if(e.id.equals("FileName"))
@@ -448,14 +448,14 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadImage(FBXElement element) {
+ private void loadImage(FbxElement element) {
long id = (Long) element.properties.get(0);
String path = (String) element.properties.get(1);
String type = (String) element.properties.get(2);
if(type.equals("Clip")) {
ImageData data = new ImageData();
data.name = path.substring(0, path.indexOf(0));
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Type"))
data.type = (String) e.properties.get(0);
else if(e.id.equals("FileName"))
@@ -471,19 +471,19 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadDeformer(FBXElement element) {
+ private void loadDeformer(FbxElement element) {
long id = (Long) element.properties.get(0);
String type = (String) element.properties.get(2);
if(type.equals("Skin")) {
SkinData skinData = new SkinData();
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("SkinningType"))
skinData.type = (String) e.properties.get(0);
}
skinMap.put(id, skinData);
} else if(type.equals("Cluster")) {
ClusterData clusterData = new ClusterData();
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Indexes"))
clusterData.indexes = (int[]) e.properties.get(0);
else if(e.id.equals("Weights"))
@@ -497,7 +497,7 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadAnimLayer(FBXElement element) {
+ private void loadAnimLayer(FbxElement element) {
long id = (Long) element.properties.get(0);
String path = (String) element.properties.get(1);
String type = (String) element.properties.get(2);
@@ -508,12 +508,12 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadAnimCurve(FBXElement element) {
+ private void loadAnimCurve(FbxElement element) {
long id = (Long) element.properties.get(0);
String type = (String) element.properties.get(2);
if(type.equals("")) {
AnimCurveData data = new AnimCurveData();
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("KeyTime"))
data.keyTimes = (long[]) e.properties.get(0);
else if(e.id.equals("KeyValueFloat"))
@@ -523,15 +523,15 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadAnimNode(FBXElement element) {
+ private void loadAnimNode(FbxElement element) {
long id = (Long) element.properties.get(0);
String path = (String) element.properties.get(1);
String type = (String) element.properties.get(2);
if(type.equals("")) {
Double x = null, y = null, z = null;
- for(FBXElement e : element.children) {
+ for(FbxElement e : element.children) {
if(e.id.equals("Properties70")) {
- for(FBXElement e2 : e.children) {
+ for(FbxElement e2 : e.children) {
if(e2.id.equals("P")) {
String propName = (String) e2.properties.get(0);
if(propName.equals("d|X"))
@@ -554,8 +554,8 @@ public class SceneLoader implements AssetLoader {
}
}
- private void loadConnections(FBXElement element) {
- for(FBXElement e : element.children) {
+ private void loadConnections(FbxElement element) {
+ for(FbxElement e : element.children) {
if(e.id.equals("C")) {
String type = (String) e.properties.get(0);
long objId, refId;
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimCurve.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimCurve.java
new file mode 100644
index 000000000..d266dcf95
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimCurve.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2009-2015 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.scene.plugins.fbx.anim;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.math.FastMath;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.plugins.fbx.obj.FbxObject;
+
+public class FbxAnimCurve extends FbxObject {
+
+ private long[] keyTimes;
+ private float[] keyValues;
+
+ public FbxAnimCurve(AssetManager assetManager, String sceneFolderName) {
+ super(assetManager, sceneFolderName);
+ }
+
+ @Override
+ public void fromElement(FbxElement element) {
+ super.fromElement(element);
+
+ for (FbxElement e : element.children) {
+ if (e.id.equals("KeyTime")) {
+ keyTimes = (long[]) e.properties.get(0);
+ } else if (e.id.equals("KeyValueFloat")) {
+ keyValues = (float[]) e.properties.get(0);
+ }
+ }
+
+ long time = -1;
+ for (int i = 0; i < keyTimes.length; i++) {
+ if (time >= keyTimes[i]) {
+ throw new UnsupportedOperationException("Keyframe times must be sequential, but they are not.");
+ }
+ time = keyTimes[i];
+ }
+ }
+
+ /**
+ * Get the times for the keyframes.
+ * @return Keyframe times.
+ */
+ public long[] getKeyTimes() {
+ return keyTimes;
+ }
+
+ /**
+ * Retrieve the curve value at the given time.
+ * If the curve has no data, 0 is returned.
+ * If the time is outside the curve, then the closest value is returned.
+ * If the time isn't on an exact keyframe, linear interpolation is used
+ * to determine the value between the keyframes at the given time.
+ * @param time The time to get the curve value at (in FBX time units).
+ * @return The value at the given time.
+ */
+ public float getValueAtTime(long time) {
+ if (keyTimes.length == 0) {
+ return 0;
+ }
+
+ // If the time is outside the range,
+ // we just return the closest value. (No extrapolation)
+ if (time <= keyTimes[0]) {
+ return keyValues[0];
+ } else if (time >= keyTimes[keyTimes.length - 1]) {
+ return keyValues[keyValues.length - 1];
+ }
+
+
+
+ int startFrame = 0;
+ int endFrame = 1;
+ int lastFrame = keyTimes.length - 1;
+
+ for (int i = 0; i < lastFrame && keyTimes[i] < time; ++i) {
+ startFrame = i;
+ endFrame = i + 1;
+ }
+
+ long keyTime1 = keyTimes[startFrame];
+ float keyValue1 = keyValues[startFrame];
+ long keyTime2 = keyTimes[endFrame];
+ float keyValue2 = keyValues[endFrame];
+
+ if (keyTime2 == time) {
+ return keyValue2;
+ }
+
+ long prevToNextDelta = keyTime2 - keyTime1;
+ long prevToCurrentDelta = time - keyTime1;
+ float lerpAmount = (float)prevToCurrentDelta / prevToNextDelta;
+
+ return FastMath.interpolateLinear(lerpAmount, keyValue1, keyValue2);
+ }
+
+ @Override
+ protected Object toJmeObject() {
+ // An AnimCurve has no jME3 representation.
+ // The parent AnimCurveNode is responsible to create the jME3
+ // representation.
+ throw new UnsupportedOperationException("No jME3 object conversion available");
+ }
+
+ @Override
+ public void connectObject(FbxObject object) {
+ unsupportedConnectObject(object);
+ }
+
+ @Override
+ public void connectObjectProperty(FbxObject object, String property) {
+ unsupportedConnectObjectProperty(object, property);
+ }
+
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimCurveNode.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimCurveNode.java
new file mode 100644
index 000000000..e8dbe7fc0
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimCurveNode.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2009-2015 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.scene.plugins.fbx.anim;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.plugins.fbx.node.FbxNode;
+import com.jme3.scene.plugins.fbx.obj.FbxObject;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FbxAnimCurveNode extends FbxObject {
+
+ private static final Logger logger = Logger.getLogger(FbxAnimCurveNode.class.getName());
+
+ private final Map influencedNodePropertiesMap = new HashMap();
+ private final Map propertyToCurveMap = new HashMap();
+ private final Map propertyToDefaultMap = new HashMap();
+
+ public FbxAnimCurveNode(AssetManager assetManager, String sceneFolderName) {
+ super(assetManager, sceneFolderName);
+ }
+
+ @Override
+ public void fromElement(FbxElement element) {
+ super.fromElement(element);
+ for (FbxElement prop : element.getFbxProperties()) {
+ String propName = (String) prop.properties.get(0);
+ String propType = (String) prop.properties.get(1);
+ if (propType.equals("Number")) {
+ float propValue = ((Double) prop.properties.get(4)).floatValue();
+ propertyToDefaultMap.put(propName, propValue);
+ }
+ }
+ }
+
+ public void addInfluencedNode(FbxNode node, String property) {
+ influencedNodePropertiesMap.put(node, property);
+ }
+
+ public Map getInfluencedNodeProperties() {
+ return influencedNodePropertiesMap;
+ }
+
+ public Collection getCurves() {
+ return propertyToCurveMap.values();
+ }
+
+ public Vector3f getVector3Value(long time) {
+ Vector3f value = new Vector3f();
+ FbxAnimCurve xCurve = propertyToCurveMap.get(FbxAnimUtil.CURVE_NODE_PROPERTY_X);
+ FbxAnimCurve yCurve = propertyToCurveMap.get(FbxAnimUtil.CURVE_NODE_PROPERTY_Y);
+ FbxAnimCurve zCurve = propertyToCurveMap.get(FbxAnimUtil.CURVE_NODE_PROPERTY_Z);
+ Float xDefault = propertyToDefaultMap.get(FbxAnimUtil.CURVE_NODE_PROPERTY_X);
+ Float yDefault = propertyToDefaultMap.get(FbxAnimUtil.CURVE_NODE_PROPERTY_Y);
+ Float zDefault = propertyToDefaultMap.get(FbxAnimUtil.CURVE_NODE_PROPERTY_Z);
+ value.x = xCurve != null ? xCurve.getValueAtTime(time) : xDefault;
+ value.y = yCurve != null ? yCurve.getValueAtTime(time) : yDefault;
+ value.z = zCurve != null ? zCurve.getValueAtTime(time) : zDefault;
+ return value;
+ }
+
+ /**
+ * Converts the euler angles from {@link #getVector3Value(long)} to
+ * a quaternion rotation.
+ * @param time Time at which to get the euler angles.
+ * @return The rotation at time
+ */
+ public Quaternion getQuaternionValue(long time) {
+ Vector3f eulerAngles = getVector3Value(time);
+ System.out.println("\tT: " + time + ". Rotation: " +
+ eulerAngles.x + ", " +
+ eulerAngles.y + ", " + eulerAngles.z);
+ Quaternion q = new Quaternion();
+ q.fromAngles(eulerAngles.x * FastMath.DEG_TO_RAD,
+ eulerAngles.y * FastMath.DEG_TO_RAD,
+ eulerAngles.z * FastMath.DEG_TO_RAD);
+ return q;
+ }
+
+ @Override
+ protected Object toJmeObject() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void connectObject(FbxObject object) {
+ unsupportedConnectObject(object);
+ }
+
+ @Override
+ public void connectObjectProperty(FbxObject object, String property) {
+ if (!(object instanceof FbxAnimCurve)) {
+ unsupportedConnectObjectProperty(object, property);
+ }
+ if (!property.equals(FbxAnimUtil.CURVE_NODE_PROPERTY_X) &&
+ !property.equals(FbxAnimUtil.CURVE_NODE_PROPERTY_Y) &&
+ !property.equals(FbxAnimUtil.CURVE_NODE_PROPERTY_Z) &&
+ !property.equals(FbxAnimUtil.CURVE_NODE_PROPERTY_VISIBILITY)) {
+ logger.log(Level.WARNING, "Animating the dimension ''{0}'' is not "
+ + "supported yet. Ignoring.", property);
+ return;
+ }
+
+ if (propertyToCurveMap.containsKey(property)) {
+ throw new UnsupportedOperationException("!");
+ }
+
+ propertyToCurveMap.put(property, (FbxAnimCurve) object);
+ }
+
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimLayer.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimLayer.java
new file mode 100644
index 000000000..872626615
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimLayer.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009-2015 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.scene.plugins.fbx.anim;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.plugins.fbx.obj.FbxObject;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+public class FbxAnimLayer extends FbxObject {
+
+ private static final Logger logger = Logger.getLogger(FbxAnimLayer.class.getName());
+
+ private final List animCurves = new ArrayList();
+
+ public FbxAnimLayer(AssetManager assetManager, String sceneFolderName) {
+ super(assetManager, sceneFolderName);
+ }
+
+ @Override
+ public void fromElement(FbxElement element) {
+ super.fromElement(element);
+ // No known properties for layers..
+ // Also jME3 doesn't support multiple layers anyway.
+ }
+
+ public List getAnimationCurveNodes() {
+ return Collections.unmodifiableList(animCurves);
+ }
+
+ @Override
+ protected Object toJmeObject() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void connectObject(FbxObject object) {
+ if (!(object instanceof FbxAnimCurveNode)) {
+ unsupportedConnectObject(object);
+ }
+
+ animCurves.add((FbxAnimCurveNode) object);
+ }
+
+ @Override
+ public void connectObjectProperty(FbxObject object, String property) {
+ unsupportedConnectObjectProperty(object, property);
+ }
+}
+
\ No newline at end of file
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimStack.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimStack.java
new file mode 100644
index 000000000..ea7dbb814
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimStack.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2015 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.scene.plugins.fbx.anim;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.plugins.fbx.obj.FbxObject;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FbxAnimStack extends FbxObject {
+
+ private static final Logger logger = Logger.getLogger(FbxAnimStack.class.getName());
+
+ private float duration;
+ private FbxAnimLayer layer0;
+
+ public FbxAnimStack(AssetManager assetManager, String sceneFolderName) {
+ super(assetManager, sceneFolderName);
+ }
+
+ @Override
+ public void fromElement(FbxElement element) {
+ super.fromElement(element);
+ for (FbxElement child : element.getFbxProperties()) {
+ String propName = (String) child.properties.get(0);
+ if (propName.equals("LocalStop")) {
+ long durationLong = (Long)child.properties.get(4);
+ duration = (float) (durationLong * FbxAnimUtil.SECONDS_PER_UNIT);
+ }
+ }
+ }
+
+// /**
+// * Finds out which FBX nodes this animation is going to influence.
+// *
+// * @return A list of FBX nodes that the stack's curves are influencing.
+// */
+// public Set getInfluencedNodes() {
+// HashSet influencedNodes = new HashSet();
+// if (layer0 == null) {
+// return influencedNodes;
+// }
+// for (FbxAnimCurveNode curveNode : layer0.getAnimationCurveNodes()) {
+// influencedNodes.addAll(curveNode.getInfluencedNodes());
+// }
+// return influencedNodes;
+// }
+
+ public float getDuration() {
+ return duration;
+ }
+
+ public FbxAnimLayer[] getLayers() {
+ return new FbxAnimLayer[]{ layer0 };
+ }
+
+ @Override
+ protected Object toJmeObject() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void connectObject(FbxObject object) {
+ if (!(object instanceof FbxAnimLayer)) {
+ unsupportedConnectObject(object);
+ }
+
+ if (layer0 != null) {
+ logger.log(Level.WARNING, "jME3 does not support layered animation. "
+ + "Only first layer has been loaded.");
+ return;
+ }
+
+ layer0 = (FbxAnimLayer) object;
+ }
+
+ @Override
+ public void connectObjectProperty(FbxObject object, String property) {
+ unsupportedConnectObjectProperty(object, property);
+ }
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimUtil.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimUtil.java
new file mode 100644
index 000000000..c376757de
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxAnimUtil.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009-2015 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.scene.plugins.fbx.anim;
+
+public class FbxAnimUtil {
+ /**
+ * Conversion factor from FBX animation time unit to seconds.
+ */
+ public static final double SECONDS_PER_UNIT = 1 / 46186158000d;
+
+ public static final String CURVE_NODE_PROPERTY_X = "d|X";
+ public static final String CURVE_NODE_PROPERTY_Y = "d|Y";
+ public static final String CURVE_NODE_PROPERTY_Z = "d|Z";
+ public static final String CURVE_NODE_PROPERTY_VISIBILITY = "d|Visibility";
+}
diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxBindPose.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxBindPose.java
new file mode 100644
index 000000000..78d37c74e
--- /dev/null
+++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxBindPose.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2009-2015 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.scene.plugins.fbx.anim;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.math.Matrix4f;
+import com.jme3.scene.plugins.fbx.file.FbxElement;
+import com.jme3.scene.plugins.fbx.file.FbxId;
+import com.jme3.scene.plugins.fbx.obj.FbxObject;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FbxBindPose extends FbxObject