diff --git a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
index 58dc87550..7036418e8 100644
--- a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
+++ b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java
@@ -138,6 +138,11 @@ class BitmapTextPage extends Geometry {
Mesh m = getMesh();
int vertCount = pageQuads.size() * 4;
int triCount = pageQuads.size() * 2;
+
+ if (vertCount > m.getVertexCount() ||
+ triCount > m.getTriangleCount()) {
+ m.setUpdateNeeded();
+ }
VertexBuffer pb = m.getBuffer(Type.Position);
VertexBuffer tb = m.getBuffer(Type.TexCoord);
diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java
index edfd380b4..1784eb7f5 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java
@@ -283,6 +283,12 @@ public interface Renderer {
* the per-instance attributes.
*/
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData);
+
+ /**
+ * Delete a Mesh (or Vertex Array Object in GL terms) from the GPU.
+ * @param mesh The mesh to delete.
+ */
+ public void deleteMesh(Mesh mesh);
/**
* Resets all previously used {@link NativeObject Native Objects} on this Renderer.
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 6576095a5..aad9e1cd6 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
@@ -149,7 +149,7 @@ public final class GLRenderer implements Renderer {
int major = Integer.parseInt(m.group(1));
int minor = Integer.parseInt(m.group(2));
if (minor >= 10 && minor % 10 == 0) {
- // some versions can look like "1.30" instead of "1.3".
+ // some versions can look like "1.30" instead of "1.3".
// make sure to correct for this
minor /= 10;
}
@@ -379,7 +379,7 @@ public final class GLRenderer implements Renderer {
limits.put(Limits.TextureAnisotropy, getInteger(GLExt.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT));
}
- if (hasExtension("GL_EXT_framebuffer_object")
+ if (hasExtension("GL_EXT_framebuffer_object")
|| caps.contains(Caps.OpenGL30)
|| caps.contains(Caps.OpenGLES20)) {
caps.add(Caps.FrameBuffer);
@@ -474,7 +474,7 @@ public final class GLRenderer implements Renderer {
{
sb.append("\t").append(cap.toString()).append("\n");
}
-
+
sb.append("\nHardware limits: \n");
for (Limits limit : Limits.values()) {
Integer value = limits.get(limit);
@@ -484,7 +484,7 @@ public final class GLRenderer implements Renderer {
sb.append("\t").append(limit.name()).append(" = ")
.append(value).append("\n");
}
-
+
logger.log(Level.INFO, sb.toString());
}
@@ -517,7 +517,7 @@ public final class GLRenderer implements Renderer {
// Initialize default state..
gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
-
+
if (caps.contains(Caps.SeamlessCubemap)) {
// Enable this globally. Should be OK.
gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
@@ -525,9 +525,9 @@ public final class GLRenderer implements Renderer {
if (caps.contains(Caps.CoreProfile)) {
// Core Profile requires VAO to be bound.
- gl3.glGenVertexArrays(intBuf16);
- int vaoId = intBuf16.get(0);
- gl3.glBindVertexArray(vaoId);
+// gl3.glGenVertexArrays(intBuf16);
+// int vaoId = intBuf16.get(0);
+// gl3.glBindVertexArray(vaoId);
}
if (gl2 != null) {
gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE);
@@ -637,7 +637,7 @@ public final class GLRenderer implements Renderer {
gl.glDepthFunc(convertTestFunction(state.getDepthFunc()));
context.depthFunc = state.getDepthFunc();
}
-
+
if (state.isDepthWrite() && !context.depthWriteEnabled) {
gl.glDepthMask(true);
context.depthWriteEnabled = true;
@@ -971,12 +971,12 @@ public final class GLRenderer implements Renderer {
gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE);
break;
case Matrix3:
- fb = (FloatBuffer) uniform.getValue();
+ fb = uniform.getMultiData();
assert fb.remaining() == 9;
gl.glUniformMatrix3(loc, false, fb);
break;
case Matrix4:
- fb = (FloatBuffer) uniform.getValue();
+ fb = uniform.getMultiData();
assert fb.remaining() == 16;
gl.glUniformMatrix4(loc, false, fb);
break;
@@ -985,23 +985,23 @@ public final class GLRenderer implements Renderer {
gl.glUniform1(loc, ib);
break;
case FloatArray:
- fb = (FloatBuffer) uniform.getValue();
+ fb = uniform.getMultiData();
gl.glUniform1(loc, fb);
break;
case Vector2Array:
- fb = (FloatBuffer) uniform.getValue();
+ fb = uniform.getMultiData();
gl.glUniform2(loc, fb);
break;
case Vector3Array:
- fb = (FloatBuffer) uniform.getValue();
+ fb = uniform.getMultiData();
gl.glUniform3(loc, fb);
break;
case Vector4Array:
- fb = (FloatBuffer) uniform.getValue();
+ fb = uniform.getMultiData();
gl.glUniform4(loc, fb);
break;
case Matrix4Array:
- fb = (FloatBuffer) uniform.getValue();
+ fb = uniform.getMultiData();
gl.glUniformMatrix4(loc, false, fb);
break;
case Int:
@@ -1086,7 +1086,7 @@ public final class GLRenderer implements Renderer {
if (gles2) {
// request GLSL ES (1.00) when compiling under GLES2.
stringBuf.append("#version 100\n");
-
+
if (source.getType() == ShaderType.Fragment) {
// GLES2 requires precision qualifier.
stringBuf.append("precision mediump float;\n");
@@ -1473,7 +1473,7 @@ public final class GLRenderer implements Renderer {
rb.getId());
}
}
-
+
private void bindFrameBuffer(FrameBuffer fb) {
if (fb == null) {
if (context.boundFBO != 0) {
@@ -1511,12 +1511,12 @@ public final class GLRenderer implements Renderer {
}
bindFrameBuffer(fb);
-
+
for (int i = 0; i < fb.getNumColorBuffers(); i++) {
FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i);
updateFrameBufferAttachment(fb, colorBuf);
}
-
+
FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer();
if (depthBuf != null) {
updateFrameBufferAttachment(fb, depthBuf);
@@ -1562,11 +1562,11 @@ public final class GLRenderer implements Renderer {
if (gl2 == null) {
return;
}
-
+
final int NONE = -2;
final int INITIAL = -1;
final int MRT_OFF = 100;
-
+
if (fb == null) {
// Set Read/Draw buffers to initial value.
if (context.boundDrawBuf != INITIAL) {
@@ -1630,9 +1630,9 @@ public final class GLRenderer implements Renderer {
}
}
}
-
+
}
-
+
public void setFrameBuffer(FrameBuffer fb) {
if (fb == null && mainFbOverride != null) {
fb = mainFbOverride;
@@ -1858,7 +1858,7 @@ public final class GLRenderer implements Renderer {
if (image != null) {
haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps();
}
-
+
LastTextureState curState = image.getLastTextureState();
if (curState.magFilter != tex.getMagFilter()) {
@@ -1921,7 +1921,7 @@ public final class GLRenderer implements Renderer {
}
curState.shadowCompareMode = texCompareMode;
}
-
+
// If at this point we didn't bind the texture, bind it now
bindTextureOnly(target, image, unit);
}
@@ -1929,7 +1929,7 @@ public final class GLRenderer implements Renderer {
/**
* Validates if a potentially NPOT texture is supported by the hardware.
*
- * Textures with power-of-2 dimensions are supported on all hardware, however
+ * Textures with power-of-2 dimensions are supported on all hardware, however
* non-power-of-2 textures may or may not be supported depending on which
* texturing features are used.
*
@@ -1984,7 +1984,7 @@ public final class GLRenderer implements Renderer {
/**
* Ensures that the texture is bound to the given unit
* and that the unit is currently active (for modification).
- *
+ *
* @param target The texture target, one of GL_TEXTURE_***
* @param img The image texture to bind
* @param unit At what unit to bind the texture.
@@ -2002,11 +2002,11 @@ public final class GLRenderer implements Renderer {
statistics.onTextureUse(img, false);
}
}
-
+
/**
* Ensures that the texture is bound to the given unit,
* but does not care if the unit is active (for rendering).
- *
+ *
* @param target The texture target, one of GL_TEXTURE_***
* @param img The image texture to bind
* @param unit At what unit to bind the texture.
@@ -2024,7 +2024,7 @@ public final class GLRenderer implements Renderer {
statistics.onTextureUse(img, false);
}
}
-
+
/**
* Uploads the given image to the GL driver.
*
@@ -2048,6 +2048,7 @@ public final class GLRenderer implements Renderer {
// bind texture
int target = convertTextureType(type, img.getMultiSamples(), -1);
+
bindTextureAndUnit(target, img, unit);
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
@@ -2062,7 +2063,7 @@ public final class GLRenderer implements Renderer {
// We'll generate mipmaps via glGenerateMipmapEXT (see below)
}
} else if (img.hasMipmaps()) {
- // Image already has mipmaps, set the max level based on the
+ // Image already has mipmaps, set the max level based on the
// number of mipmaps we have.
gl.glTexParameteri(target, GL.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1);
} else {
@@ -2334,32 +2335,37 @@ public final class GLRenderer implements Renderer {
context.attribIndexList.copyNewToOld();
}
- 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 (context.boundShaderProgram <= 0) {
- throw new IllegalStateException("Cannot render mesh without shader bound");
- }
-
- Attribute attrib = context.boundShader.getAttribute(vb.getBufferType());
+ private int updateAttributeLocation(Shader shader, VertexBuffer.Type attribType) {
+ Attribute attrib = shader.getAttribute(attribType);
int loc = attrib.getLocation();
if (loc == -1) {
- return; // not defined
+ return -1; // not defined
}
if (loc == -2) {
- loc = gl.glGetAttribLocation(context.boundShaderProgram, "in" + vb.getBufferType().name());
+ loc = gl.glGetAttribLocation(context.boundShaderProgram, "in" + attribType.name());
// not really the name of it in the shader (inPosition) but
// the internal name of the enum (Position).
if (loc < 0) {
attrib.setLocation(-1);
- return; // not available in shader.
+ return -1; // not available in shader.
} else {
attrib.setLocation(loc);
}
}
+ return loc;
+ }
+
+ 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");
+ }
+
+ Shader shader = context.boundShader;
+ int location = updateAttributeLocation(shader, vb.getBufferType());
+ if (location == -1) {
+ return;
+ }
if (vb.isInstanced()) {
if (!caps.contains(Caps.MeshInstancing)) {
@@ -2383,11 +2389,11 @@ public final class GLRenderer implements Renderer {
VertexBuffer[] attribs = context.boundAttribs;
for (int i = 0; i < slotsRequired; i++) {
- if (!context.attribIndexList.moveToNew(loc + i)) {
- gl.glEnableVertexAttribArray(loc + i);
+ if (!context.attribIndexList.moveToNew(location + i)) {
+ gl.glEnableVertexAttribArray(location + i);
}
}
- if (attribs[loc] != vb) {
+ if (attribs[location] != vb) {
// NOTE: Use id from interleaved buffer if specified
int bufId = idb != null ? idb.getId() : vb.getId();
assert bufId != -1;
@@ -2400,12 +2406,12 @@ public final class GLRenderer implements Renderer {
}
if (slotsRequired == 1) {
- gl.glVertexAttribPointer(loc,
- vb.getNumComponents(),
- convertFormat(vb.getFormat()),
- vb.isNormalized(),
- vb.getStride(),
- vb.getOffset());
+ gl.glVertexAttribPointer(location,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ vb.getOffset());
} else {
for (int i = 0; i < slotsRequired; i++) {
// The pointer maps the next 4 floats in the slot.
@@ -2416,17 +2422,17 @@ public final class GLRenderer implements Renderer {
// P4: ____________XXXX____________XXXX
// stride = 4 bytes in float * 4 floats in slot * num slots
// offset = 4 bytes in float * 4 floats in slot * slot index
- gl.glVertexAttribPointer(loc + i,
- 4,
- convertFormat(vb.getFormat()),
- vb.isNormalized(),
- 4 * 4 * slotsRequired,
- 4 * 4 * i);
+ gl.glVertexAttribPointer(location + i,
+ 4,
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ 4 * 4 * slotsRequired,
+ 4 * 4 * i);
}
}
for (int i = 0; i < slotsRequired; i++) {
- int slot = loc + i;
+ int slot = location + i;
if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) {
// non-instanced -> instanced
glext.glVertexAttribDivisorARB(slot, vb.getInstanceSpan());
@@ -2439,6 +2445,92 @@ public final class GLRenderer implements Renderer {
}
}
+ /**
+ * Set VBO on VAO. Assumes a brand new mesh or modified mesh with new buffer.
+ *
+ * @param vb
+ * @param idb
+ */
+ public void setVertexAttribVAO(VertexBuffer vb, VertexBuffer idb) {
+ if (vb.getBufferType() == VertexBuffer.Type.Index) {
+ throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
+ }
+
+ Shader shader = context.boundShader;
+ int location = updateAttributeLocation(shader, vb.getBufferType());
+ if (location == -1) {
+ return;
+ }
+
+ if (vb.isInstanced()) {
+ if (!caps.contains(Caps.MeshInstancing)) {
+ throw new RendererException("Instancing is required, "
+ + "but not supported by the "
+ + "graphics hardware");
+ }
+ }
+ int slotsRequired = 1;
+ if (vb.getNumComponents() > 4) {
+ if (vb.getNumComponents() % 4 != 0) {
+ throw new RendererException("Number of components in multi-slot "
+ + "buffers must be divisible by 4");
+ }
+ slotsRequired = vb.getNumComponents() / 4;
+ }
+
+ if (vb.isUpdateNeeded() && idb == null) {
+ updateBufferData(vb);
+ }
+
+ for (int i = 0; i < slotsRequired; i++) {
+ gl.glEnableVertexAttribArray(location + i);
+ }
+
+ // NOTE: Use id from interleaved buffer if specified
+ int bufId = idb != null ? idb.getId() : vb.getId();
+ assert bufId != -1;
+ if (context.boundArrayVBO != bufId) {
+ gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufId);
+ context.boundArrayVBO = bufId;
+ //statistics.onVertexBufferUse(vb, true);
+ } else {
+ //statistics.onVertexBufferUse(vb, false);
+ }
+
+ if (slotsRequired == 1) {
+ gl.glVertexAttribPointer(location,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ vb.getOffset());
+ } else {
+ for (int i = 0; i < slotsRequired; i++) {
+ // The pointer maps the next 4 floats in the slot.
+ // E.g.
+ // P1: XXXX____________XXXX____________
+ // P2: ____XXXX____________XXXX________
+ // P3: ________XXXX____________XXXX____
+ // P4: ____________XXXX____________XXXX
+ // stride = 4 bytes in float * 4 floats in slot * num slots
+ // offset = 4 bytes in float * 4 floats in slot * slot index
+ gl.glVertexAttribPointer(location + i,
+ 4,
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ 4 * 4 * slotsRequired,
+ 4 * 4 * i);
+ }
+ }
+
+ for (int i = 0; i < slotsRequired; i++) {
+ int slot = location + i;
+ if (vb.isInstanced()) {
+ glext.glVertexAttribDivisorARB(slot, vb.getInstanceSpan());
+ }
+ }
+ }
+
public void setVertexAttrib(VertexBuffer vb) {
setVertexAttrib(vb, null);
}
@@ -2488,57 +2580,20 @@ public final class GLRenderer implements Renderer {
}
int vertCount = mesh.getVertexCount();
- if (mesh.getMode() == Mode.Hybrid) {
- int[] modeStart = mesh.getModeStart();
- int[] elementLengths = mesh.getElementLengths();
-
- int elMode = convertElementMode(Mode.Triangles);
- int fmt = convertFormat(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];
-
- if (count > 1) {
- glext.glDrawElementsInstancedARB(elMode,
- elementLength,
- fmt,
- curOffset,
- count);
- } else {
- gl.glDrawRangeElements(elMode,
- 0,
- vertCount,
- elementLength,
- fmt,
- curOffset);
- }
-
- curOffset += elementLength * elSize;
- }
+ boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
+ if (useInstancing) {
+ glext.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
+ indexBuf.getData().limit(),
+ convertFormat(indexBuf.getFormat()),
+ 0,
+ count);
} else {
- if (count > 1) {
- glext.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()),
- indexBuf.getData().limit(),
- convertFormat(indexBuf.getFormat()),
- 0,
- count);
- } else {
- gl.glDrawRangeElements(convertElementMode(mesh.getMode()),
- 0,
- vertCount,
- indexBuf.getData().limit(),
- convertFormat(indexBuf.getFormat()),
- 0);
- }
+ gl.glDrawRangeElements(convertElementMode(mesh.getMode()),
+ 0,
+ vertCount,
+ indexBuf.getData().limit(),
+ convertFormat(indexBuf.getFormat()),
+ 0);
}
}
@@ -2568,27 +2623,16 @@ public final class GLRenderer implements Renderer {
}
}
- public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) {
- int id = mesh.getId();
- if (id == -1) {
- IntBuffer temp = intBuf1;
- gl3.glGenVertexArrays(temp);
- id = temp.get(0);
- mesh.setId(id);
- }
-
- if (context.boundVertexArray != id) {
- gl3.glBindVertexArray(id);
- context.boundVertexArray = id;
- }
-
+ private void setupVertexBuffersLegacy(Mesh mesh, VertexBuffer[] instanceData) {
VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
if (interleavedData != null && interleavedData.isUpdateNeeded()) {
updateBufferData(interleavedData);
}
if (instanceData != null) {
- setVertexAttrib(instanceData, null);
+ for (VertexBuffer vb : instanceData) {
+ setVertexAttrib(vb, null);
+ }
}
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
@@ -2608,80 +2652,132 @@ public final class GLRenderer implements Renderer {
}
}
- private void renderMeshVertexArray(Mesh mesh, int lod, int count, VertexBuffer instanceData) {
- if (mesh.getId() == -1) {
- updateVertexArray(mesh, instanceData);
- } else {
- // TODO: Check if it was updated
+ private void setupVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) {
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ if (instanceData != null) {
+ for (VertexBuffer vb : instanceData) {
+ setVertexAttribVAO(vb, null);
+ }
}
- if (context.boundVertexArray != mesh.getId()) {
- gl3.glBindVertexArray(mesh.getId());
- context.boundVertexArray = mesh.getId();
- }
+ for (VertexBuffer vb : mesh.getBufferList().getArray()) {
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index) {
+ continue;
+ }
-// IntMap buffers = mesh.getBuffers();
- VertexBuffer indices;
- if (mesh.getNumLodLevels() > 0) {
- indices = mesh.getLodLevel(lod);
- } else {
- indices = mesh.getBuffer(Type.Index);
- }
- if (indices != null) {
- drawTriangleList(indices, mesh, count);
- } else {
- drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
+ if (vb.getStride() == 0) {
+ // not interleaved
+ setVertexAttribVAO(vb, null);
+ } else {
+ // interleaved
+ setVertexAttribVAO(vb, interleavedData);
+ }
}
- clearVertexAttribs();
- }
-
- private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
- // Here while count is still passed in. Can be removed when/if
- // the method is collapsed again. -pspeed
- count = Math.max(mesh.getInstanceCount(), count);
+ mesh.clearUpdateNeeded();
+ }
+ private void updateVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) {
VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
if (interleavedData != null && interleavedData.isUpdateNeeded()) {
updateBufferData(interleavedData);
}
+ if (instanceData != null) {
+ for (VertexBuffer vb : instanceData) {
+ if (vb.isUpdateNeeded()) {
+ updateBufferData(vb);
+ }
+ }
+ }
+ for (VertexBuffer vb : mesh.getBufferList().getArray()) {
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index
+ || !vb.isUpdateNeeded()
+ || !context.boundShader.isAttributeDefined(vb.getBufferType())) {
+ continue;
+ }
+ updateBufferData(vb);
+ }
+ }
+ private VertexBuffer getIndexBuffer(Mesh mesh, int lod) {
VertexBuffer indices;
if (mesh.getNumLodLevels() > 0) {
indices = mesh.getLodLevel(lod);
} else {
indices = mesh.getBuffer(Type.Index);
}
+ return indices;
+ }
- if (instanceData != null) {
- for (VertexBuffer vb : instanceData) {
- setVertexAttrib(vb, null);
- }
+ private void setVertexArrayObject(Mesh mesh) {
+ int id = mesh.getId();
+
+ if (id == -1) {
+ IntBuffer temp = intBuf1;
+ gl3.glGenVertexArrays(temp);
+ id = temp.get(0);
+ mesh.setId(id);
+
+ objManager.registerObject(mesh);
}
- 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 (context.boundVertexArray != id) {
+ gl3.glBindVertexArray(id);
+ context.boundVertexArray = id;
+ }
+ }
+
+ private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
+ setVertexArrayObject(mesh);
+
+ // VAO clears current bound VBO automatically
+ context.boundElementArrayVBO = 0;
+ context.boundArrayVBO = 0;
+
+ VertexBuffer indices = getIndexBuffer(mesh, lod);
+ if (mesh.isUpdateNeeded()) {
+ setupVertexBuffers(mesh, instanceData);
+ if (indices != null) {
+ updateBufferData(indices);
+ }
+ } else {
+ updateVertexBuffers(mesh, instanceData);
+ if (indices != null) {
+ // NOTE: context.boundElementArrayVBO gets captured in the VAO.
+ // Make everyone think its already bound.
+ context.boundElementArrayVBO = indices.getId();
}
+ }
- if (vb.getStride() == 0) {
- // not interleaved
- setVertexAttrib(vb);
- } else {
- // interleaved
- setVertexAttrib(vb, interleavedData);
+ if (indices != null) {
+ if (indices.isUpdateNeeded()) {
+ updateBufferData(indices);
}
+
+ drawTriangleList(indices, mesh, count);
+
+ context.boundElementArrayVBO = 0;
+ } else {
+ drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
}
+ }
+
+ private void renderMeshLegacy(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
+ setupVertexBuffersLegacy(mesh, instanceData);
+ VertexBuffer indices = getIndexBuffer(mesh, lod);
clearVertexAttribs();
-
+
if (indices != null) {
drawTriangleList(indices, mesh, count);
} else {
drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
}
+
}
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
@@ -2701,12 +2797,30 @@ public final class GLRenderer implements Renderer {
if (gl4 != null && mesh.getMode().equals(Mode.Patch)) {
gl4.glPatchParameter(mesh.getPatchVertexCount());
}
+
statistics.onMeshDrawn(mesh, lod, count);
-// if (ctxCaps.GL_ARB_vertex_array_object){
-// renderMeshVertexArray(mesh, lod, count);
-// }else{
- renderMeshDefault(mesh, lod, count, instanceData);
-// }
+
+ // Here while count is still passed in. Can be removed when/if
+ // the method is collapsed again. -pspeed
+ count = Math.max(mesh.getInstanceCount(), count);
+
+// if (caps.contains(Caps.VertexBufferArray)) {
+ renderMeshDefault(mesh, lod, count, instanceData);
+// } else {
+// renderMeshLegacy(mesh, lod, count, instanceData);
+// // }
+ }
+
+ @Override
+ public void deleteMesh(Mesh mesh) {
+ int bufId = mesh.getId();
+ if (bufId != -1) {
+ // delete vertex array object
+ intBuf1.put(0, bufId);
+ intBuf1.position(0).limit(1);
+ gl3.glDeleteVertexArrays(intBuf1);
+ mesh.resetObject();
+ }
}
public void setMainFrameBufferSrgb(boolean enableSrgb) {
diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java
index a0f8e1fe6..a38bf0447 100644
--- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java
+++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java
@@ -42,6 +42,7 @@ import com.jme3.math.Matrix4f;
import com.jme3.math.Triangle;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
+import com.jme3.renderer.Renderer;
import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
@@ -49,6 +50,7 @@ import com.jme3.scene.mesh.*;
import com.jme3.util.BufferUtils;
import com.jme3.util.IntMap;
import com.jme3.util.IntMap.Entry;
+import com.jme3.util.NativeObject;
import com.jme3.util.SafeArrayList;
import java.io.IOException;
import java.nio.*;
@@ -71,7 +73,7 @@ import java.util.ArrayList;
*
* @author Kirill Vainer
*/
-public class Mesh implements Savable, Cloneable {
+public class Mesh extends NativeObject implements Savable {
/**
* The mode of the Mesh specifies both the type of primitive represented
@@ -127,19 +129,14 @@ public class Mesh implements Savable, Cloneable {
*/
TriangleFan(false),
- /**
- * A combination of various triangle modes. It is best to avoid
- * using this mode as it may not be supported by all renderers.
- * The {@link Mesh#setModeStart(int[]) mode start points} and
- * {@link Mesh#setElementLengths(int[]) element lengths} must
- * be specified for this mode.
- */
- Hybrid(false),
+ Reserved(false),
+
/**
* Used for Tesselation only. Requires to set the number of vertices
* for each patch (default is 3 for triangle tesselation)
*/
Patch(true);
+
private boolean listMode = false;
private Mode(boolean listMode){
@@ -182,9 +179,6 @@ public class Mesh implements Savable, Cloneable {
private int patchVertexCount=3; //only used for tesselation
private int maxNumWeights = -1; // only if using skeletal animation
- private int[] elementLengths;
- private int[] modeStart;
-
private Mode mode = Mode.Triangles;
/**
@@ -193,6 +187,10 @@ public class Mesh implements Savable, Cloneable {
public Mesh(){
}
+ protected Mesh(int id) {
+ super(id);
+ }
+
/**
* Create a shallow clone of this Mesh. The {@link VertexBuffer vertex
* buffers} are shared between this and the clone mesh, the rest
@@ -202,23 +200,12 @@ public class Mesh implements Savable, Cloneable {
*/
@Override
public Mesh clone() {
- try {
- Mesh clone = (Mesh) super.clone();
- clone.meshBound = meshBound.clone();
- clone.collisionTree = collisionTree != null ? collisionTree : null;
- clone.buffers = buffers.clone();
- clone.buffersList = new SafeArrayList(VertexBuffer.class,buffersList);
- clone.vertexArrayID = -1;
- if (elementLengths != null) {
- clone.elementLengths = elementLengths.clone();
- }
- if (modeStart != null) {
- clone.modeStart = modeStart.clone();
- }
- return clone;
- } catch (CloneNotSupportedException ex) {
- throw new AssertionError();
- }
+ Mesh clone = (Mesh) super.clone();
+ clone.meshBound = meshBound.clone();
+ clone.collisionTree = collisionTree != null ? collisionTree : null;
+ clone.buffers = buffers.clone();
+ clone.buffersList = new SafeArrayList(VertexBuffer.class,buffersList);
+ return clone;
}
/**
@@ -229,37 +216,30 @@ public class Mesh implements Savable, Cloneable {
* @return a deep clone of this mesh.
*/
public Mesh deepClone(){
- try{
- Mesh clone = (Mesh) super.clone();
- clone.meshBound = meshBound != null ? meshBound.clone() : null;
-
- // TODO: Collision tree cloning
- //clone.collisionTree = collisionTree != null ? collisionTree : null;
- clone.collisionTree = null; // it will get re-generated in any case
-
- clone.buffers = new IntMap();
- clone.buffersList = new SafeArrayList(VertexBuffer.class);
- for (VertexBuffer vb : buffersList.getArray()){
- VertexBuffer bufClone = vb.clone();
- clone.buffers.put(vb.getBufferType().ordinal(), bufClone);
- clone.buffersList.add(bufClone);
- }
-
- clone.vertexArrayID = -1;
- clone.vertCount = vertCount;
- clone.elementCount = elementCount;
- clone.instanceCount = instanceCount;
-
- // although this could change
- // if the bone weight/index buffers are modified
- clone.maxNumWeights = maxNumWeights;
-
- clone.elementLengths = elementLengths != null ? elementLengths.clone() : null;
- clone.modeStart = modeStart != null ? modeStart.clone() : null;
- return clone;
- }catch (CloneNotSupportedException ex){
- throw new AssertionError();
+ Mesh clone = (Mesh) super.clone();
+ clone.meshBound = meshBound != null ? meshBound.clone() : null;
+
+ // TODO: Collision tree cloning
+ //clone.collisionTree = collisionTree != null ? collisionTree : null;
+ clone.collisionTree = null; // it will get re-generated in any case
+
+ clone.buffers = new IntMap();
+ clone.buffersList = new SafeArrayList(VertexBuffer.class);
+ for (VertexBuffer vb : buffersList.getArray()){
+ VertexBuffer bufClone = vb.clone();
+ clone.buffers.put(vb.getBufferType().ordinal(), bufClone);
+ clone.buffersList.add(bufClone);
}
+
+ clone.vertCount = vertCount;
+ clone.elementCount = elementCount;
+ clone.instanceCount = instanceCount;
+
+ // although this could change
+ // if the bone weight/index buffers are modified
+ clone.maxNumWeights = maxNumWeights;
+
+ return clone;
}
/**
@@ -373,12 +353,17 @@ public class Mesh implements Savable, Cloneable {
// convert weights on the heap
VertexBuffer weights = getBuffer(Type.BoneWeight);
- if (!weights.getData().hasArray()) {
- FloatBuffer originalWeight = (FloatBuffer) weights.getData();
- FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
- originalWeight.clear();
- arrayWeight.put(originalWeight);
- weights.updateData(arrayWeight);
+ if (!weights.getData().hasArray()) {
+ if (weights.getFormat() == Format.Float) {
+ FloatBuffer originalWeight = (FloatBuffer) weights.getData();
+ FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
+ originalWeight.clear();
+ arrayWeight.put(originalWeight);
+ weights.updateData(arrayWeight);
+ } else {
+ // UByte to Float conversion
+ throw new UnsupportedOperationException("Not yet supported");
+ }
}
weights.setUsage(Usage.CpuOnly);
// position, normal, and tanget buffers to be in "Stream" mode
@@ -475,40 +460,6 @@ public class Mesh implements Savable, Cloneable {
return lodLevels[lod];
}
- /**
- * Get the element lengths for {@link Mode#Hybrid} mesh mode.
- *
- * @return element lengths
- */
- public int[] getElementLengths() {
- return elementLengths;
- }
-
- /**
- * Set the element lengths for {@link Mode#Hybrid} mesh mode.
- *
- * @param elementLengths The element lengths to set
- */
- public void setElementLengths(int[] elementLengths) {
- this.elementLengths = elementLengths;
- }
-
- /**
- * Set the mode start indices for {@link Mode#Hybrid} mesh mode.
- *
- * @return mode start indices
- */
- public int[] getModeStart() {
- return modeStart;
- }
-
- /**
- * Get the mode start indices for {@link Mode#Hybrid} mesh mode.
- */
- public void setModeStart(int[] modeStart) {
- this.modeStart = modeStart;
- }
-
/**
* Returns the mesh mode
*
@@ -899,23 +850,6 @@ public class Mesh implements Savable, Cloneable {
indices[2] = ib.get(vertIndex+2);
}
- /**
- * Returns the mesh's VAO ID. Internal use only.
- */
- public int getId(){
- return vertexArrayID;
- }
-
- /**
- * Sets the mesh's VAO ID. Internal use only.
- */
- public void setId(int id){
- if (vertexArrayID != -1)
- throw new IllegalStateException("ID has already been set.");
-
- vertexArrayID = id;
- }
-
/**
* Generates a collision tree for the mesh.
* Called automatically by {@link #collideWith(com.jme3.collision.Collidable,
@@ -1109,20 +1043,17 @@ public class Mesh implements Savable, Cloneable {
* @return A virtual or wrapped index buffer to read the data as a list
*/
public IndexBuffer getIndicesAsList(){
- if (mode == Mode.Hybrid)
- throw new UnsupportedOperationException("Hybrid mode not supported");
-
IndexBuffer ib = getIndexBuffer();
- if (ib != null){
- if (mode.isListMode()){
+ if (ib != null) {
+ if (mode.isListMode()) {
// already in list mode
- return ib;
- }else{
+ return ib;
+ } else {
// not in list mode but it does have an index buffer
// wrap it so the data is converted to list format
return new WrappedIndexBuffer(this);
}
- }else{
+ } else {
// return a virtual index buffer that will supply
// "fake" indices in list format
return new VirtualIndexBuffer(vertCount, mode);
@@ -1385,16 +1316,30 @@ public class Mesh implements Savable, Cloneable {
return patchVertexCount;
}
+ @Override
+ public void resetObject() {
+ id = -1;
+ setUpdateNeeded();
+ }
+
+ @Override
+ public void deleteObject(Object rendererObject) {
+ ((Renderer)rendererObject).deleteMesh(this);
+ }
+
+ @Override
+ public NativeObject createDestructableClone() {
+ return new Mesh(id);
+ }
+
+ @Override
+ public long getUniqueId() {
+ return ((long)OBJTYPE_MESH << 32) | ((long)id);
+ }
+
public void write(JmeExporter ex) throws IOException {
OutputCapsule out = ex.getCapsule(this);
-// HashMap map = new HashMap();
-// for (Entry buf : buffers){
-// if (buf.getValue() != null)
-// map.put(buf.getKey()+"a", buf.getValue());
-// }
-// out.writeStringSavableMap(map, "buffers", null);
-
out.write(meshBound, "modelBound", null);
out.write(vertCount, "vertCount", -1);
out.write(elementCount, "elementCount", -1);
@@ -1402,8 +1347,6 @@ public class Mesh implements Savable, Cloneable {
out.write(maxNumWeights, "max_num_weights", -1);
out.write(mode, "mode", Mode.Triangles);
out.write(collisionTree, "collisionTree", null);
- out.write(elementLengths, "elementLengths", null);
- out.write(modeStart, "modeStart", null);
out.write(pointSize, "pointSize", 1f);
//Removing HW skinning buffers to not save them
@@ -1439,21 +1382,16 @@ public class Mesh implements Savable, Cloneable {
instanceCount = in.readInt("instanceCount", -1);
maxNumWeights = in.readInt("max_num_weights", -1);
mode = in.readEnum("mode", Mode.class, Mode.Triangles);
- elementLengths = in.readIntArray("elementLengths", null);
- modeStart = in.readIntArray("modeStart", null);
collisionTree = (BIHTree) in.readSavable("collisionTree", null);
- elementLengths = in.readIntArray("elementLengths", null);
- modeStart = in.readIntArray("modeStart", null);
pointSize = in.readFloat("pointSize", 1f);
-// in.readStringSavableMap("buffers", null);
buffers = (IntMap) in.readIntSavableMap("buffers", null);
for (Entry entry : buffers){
buffersList.add(entry.getValue());
}
//creating hw animation buffers empty so that they are put in the cache
- if(isAnimated()){
+ if (isAnimated()) {
VertexBuffer hwBoneIndex = new VertexBuffer(Type.HWBoneIndex);
hwBoneIndex.setUsage(Usage.CpuOnly);
setBuffer(hwBoneIndex);
diff --git a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java
index c67435b78..3fe8e5cad 100644
--- a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java
+++ b/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java
@@ -524,6 +524,17 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
this.usage = usage;
}
+ /**
+ * The size of an element in bytes.
+ *
+ * The number of components multiplied by the size of a component.
+ *
+ * @return size of an element in bytes.
+ */
+ public int getElementSize() {
+ return componentsLength;
+ }
+
/**
* @param normalized Set to true if integer components should be converted
* from their maximal range into the range 0.0 - 1.0 when converted to
@@ -976,6 +987,10 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
* of the parameters. The buffer will be of the type specified by
* {@link Format format} and would be able to contain the given number
* of elements with the given number of components in each element.
+ * @param format The format of the buffer to create
+ * @param components The number of components (aka dimensions)
+ * @param numElements Capacity of the buffer in number of elements.
+ * @return A buffer satisfying the given requirements.
*/
public static Buffer createBuffer(Format format, int components, int numElements){
if (components < 1 || components > 4)
@@ -1010,7 +1025,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
* @return Deep clone of this buffer
*/
@Override
- public VertexBuffer clone(){
+ public VertexBuffer clone() {
// NOTE: Superclass GLObject automatically creates shallow clone
// e.g re-use ID.
VertexBuffer vb = (VertexBuffer) super.clone();
diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java
index 18fefa90a..43d762954 100644
--- a/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java
+++ b/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java
@@ -81,7 +81,7 @@ public class VirtualIndexBuffer extends IndexBuffer {
case Triangles:
numIndices = numVerts;
return;
- case Hybrid:
+ case Reserved:
throw new UnsupportedOperationException();
}
}
diff --git a/jme3-core/src/main/java/com/jme3/shader/Shader.java b/jme3-core/src/main/java/com/jme3/shader/Shader.java
index d6fc495f2..3f3ac0d57 100644
--- a/jme3-core/src/main/java/com/jme3/shader/Shader.java
+++ b/jme3-core/src/main/java/com/jme3/shader/Shader.java
@@ -303,6 +303,16 @@ public final class Shader extends NativeObject {
return attrib;
}
+ public boolean isAttributeDefined(VertexBuffer.Type attribType) {
+ int ordinal = attribType.ordinal();
+ Attribute attrib = attribs.get(ordinal);
+ if (attrib == null){
+ return false;
+ } else {
+ return attrib.location != -1 && attrib.location != 2;
+ }
+ }
+
public ListMap getUniformMap(){
return uniforms;
}
diff --git a/jme3-core/src/main/java/com/jme3/shader/Uniform.java b/jme3-core/src/main/java/com/jme3/shader/Uniform.java
index 5d5fd2fe3..9580d8b72 100644
--- a/jme3-core/src/main/java/com/jme3/shader/Uniform.java
+++ b/jme3-core/src/main/java/com/jme3/shader/Uniform.java
@@ -102,6 +102,10 @@ public class Uniform extends ShaderVariable {
public Object getValue(){
return value;
}
+
+ public FloatBuffer getMultiData() {
+ return multiData;
+ }
public boolean isSetByCurrentMaterial() {
return setByCurrentMaterial;
@@ -111,21 +115,6 @@ public class Uniform extends ShaderVariable {
setByCurrentMaterial = false;
}
- private static void setVector4(Vector4f vec, Object value) {
- if (value instanceof ColorRGBA) {
- ColorRGBA color = (ColorRGBA) value;
- vec.set(color.r, color.g, color.b, color.a);
- } else if (value instanceof Quaternion) {
- Quaternion quat = (Quaternion) value;
- vec.set(quat.getX(), quat.getY(), quat.getZ(), quat.getW());
- } else if (value instanceof Vector4f) {
- Vector4f vec4 = (Vector4f) value;
- vec.set(vec4);
- } else{
- throw new IllegalArgumentException();
- }
- }
-
public void clearValue(){
updateNeeded = true;
@@ -189,20 +178,36 @@ public class Uniform extends ShaderVariable {
switch (type){
case Matrix3:
+ if (value.equals(this.value)) {
+ return;
+ }
Matrix3f m3 = (Matrix3f) value;
if (multiData == null) {
multiData = BufferUtils.createFloatBuffer(9);
}
m3.fillFloatBuffer(multiData, true);
multiData.clear();
+ if (this.value == null) {
+ this.value = new Matrix3f(m3);
+ } else {
+ ((Matrix3f)this.value).set(m3);
+ }
break;
case Matrix4:
+ if (value.equals(this.value)) {
+ return;
+ }
Matrix4f m4 = (Matrix4f) value;
if (multiData == null) {
multiData = BufferUtils.createFloatBuffer(16);
}
m4.fillFloatBuffer(multiData, true);
multiData.clear();
+ if (this.value == null) {
+ this.value = new Matrix4f(m4);
+ } else {
+ ((Matrix4f)this.value).copy(m4);
+ }
break;
case IntArray:
int[] ia = (int[]) value;
@@ -283,11 +288,32 @@ public class Uniform extends ShaderVariable {
}
multiData.clear();
break;
+ case Vector4:
+ if (value.equals(this.value)) {
+ return;
+ }
+ if (value instanceof ColorRGBA) {
+ if (this.value == null) {
+ this.value = new ColorRGBA();
+ }
+ ((ColorRGBA) this.value).set((ColorRGBA) value);
+ } else if (value instanceof Vector4f) {
+ if (this.value == null) {
+ this.value = new Vector4f();
+ }
+ ((Vector4f) this.value).set((Vector4f) value);
+ } else {
+ if (this.value == null) {
+ this.value = new Quaternion();
+ }
+ ((Quaternion) this.value).set((Quaternion) value);
+ }
+ break;
// Only use check if equals optimization for primitive values
case Int:
case Float:
case Boolean:
- if (this.value != null && this.value.equals(value)) {
+ if (value.equals(this.value)) {
return;
}
this.value = value;
@@ -297,39 +323,38 @@ public class Uniform extends ShaderVariable {
break;
}
- if (multiData != null) {
- this.value = multiData;
- }
+// if (multiData != null) {
+// this.value = multiData;
+// }
varType = type;
updateNeeded = true;
}
public void setVector4Length(int length){
- if (location == -1)
+ if (location == -1) {
return;
-
- FloatBuffer fb = (FloatBuffer) value;
- if (fb == null || fb.capacity() < length * 4) {
- value = BufferUtils.createFloatBuffer(length * 4);
}
-
+
+ multiData = BufferUtils.ensureLargeEnough(multiData, length * 4);
+ value = multiData;
varType = VarType.Vector4Array;
updateNeeded = true;
setByCurrentMaterial = true;
}
public void setVector4InArray(float x, float y, float z, float w, int index){
- if (location == -1)
+ if (location == -1) {
return;
+ }
- if (varType != null && varType != VarType.Vector4Array)
- throw new IllegalArgumentException("Expected a "+varType.name()+" value!");
+ if (varType != null && varType != VarType.Vector4Array) {
+ throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
+ }
- FloatBuffer fb = (FloatBuffer) value;
- fb.position(index * 4);
- fb.put(x).put(y).put(z).put(w);
- fb.rewind();
+ multiData.position(index * 4);
+ multiData.put(x).put(y).put(z).put(w);
+ multiData.rewind();
updateNeeded = true;
setByCurrentMaterial = true;
}
diff --git a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java
index ae00386ee..c93350467 100644
--- a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java
@@ -164,4 +164,7 @@ public class NullRenderer implements Renderer {
public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) {
}
+ @Override
+ public void deleteMesh(Mesh mesh) {
+ }
}
diff --git a/jme3-core/src/main/java/com/jme3/util/NativeObject.java b/jme3-core/src/main/java/com/jme3/util/NativeObject.java
index 508e6623d..34114ebd2 100644
--- a/jme3-core/src/main/java/com/jme3/util/NativeObject.java
+++ b/jme3-core/src/main/java/com/jme3/util/NativeObject.java
@@ -52,7 +52,8 @@ public abstract class NativeObject implements Cloneable {
OBJTYPE_SHADERSOURCE = 5,
OBJTYPE_AUDIOBUFFER = 6,
OBJTYPE_AUDIOSTREAM = 7,
- OBJTYPE_FILTER = 8;
+ OBJTYPE_FILTER = 8,
+ OBJTYPE_MESH = 9;
/**
* The object manager to which this NativeObject is registered to.
diff --git a/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java b/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java
new file mode 100644
index 000000000..3fbf34117
--- /dev/null
+++ b/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java
@@ -0,0 +1,141 @@
+/*
+ * 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 jme3test.stress;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+
+// Let's see if we can render 2500 batches in 60 fps.
+// We'll use 50 materials with various combinations of textures and colors
+// to make things wild.
+public class TestUniqueGeometries extends SimpleApplication {
+
+ private Material[] randomMaterials = new Material[50];
+
+ private String[] textureList = new String[] {
+ "Blender/2.4x/textures/Concrete_Wall.PNG",
+ "Blender/2.4x/textures/Grass_256.png",
+ "Blender/2.4x/textures/SandDesert_StartTower.png",
+ "Blender/2.4x/textures/Tar_Cracked.png",
+ "Blender/2.4x/textures/WarningStrip.png",
+ "Blender/2.4x/WoodCrate_lighter.png",
+ "Interface/Logo/Monkey.jpg",
+ "Interface/Logo/Monkey.png",
+ "Models/Boat/boat.png",
+ "Models/Ninja/Ninja.jpg",
+ "Models/Tree/BarkColor.jpg",
+ "Textures/Terrain/BrickWall/BrickWall.jpg",
+ "Textures/Terrain/Pond/Pond.jpg",
+ "Textures/Terrain/Pond/Pond_normal.png",
+ "Textures/Terrain/Rock/Rock.PNG",
+ "Textures/Terrain/Rock/Rock_normal.png",
+ "Textures/Terrain/Rock2/rock.jpg",
+ "Textures/Terrain/Rocky/RockyNormals.jpg",
+ "Textures/Terrain/Rocky/RockyTexture.jpg",
+ "Textures/Terrain/splat/alpha1.png",
+ "Textures/Terrain/splat/alpha2.png",
+ "Textures/Terrain/splat/alphamap.png",
+ "Textures/Terrain/splat/alphamap2.png",
+ "Textures/Terrain/splat/dirt.jpg",
+ "Textures/Terrain/splat/dirt_normal.png",
+ "Textures/Terrain/splat/fortress512.png",
+ "Textures/Terrain/splat/grass.jpg",
+ "Textures/Terrain/splat/grass_normal.jpg",
+ "Textures/Terrain/splat/mountains128.png",
+ "Textures/Terrain/splat/road.jpg",
+ "Textures/Terrain/splat/road_normal.png",
+ };
+
+ public static void main(String[] args) {
+ TestUniqueGeometries app = new TestUniqueGeometries();
+ AppSettings settings = new AppSettings(true);
+ settings.putBoolean("GraphicsTrace", false);
+ settings.putBoolean("GraphicsTiming", true);
+ app.setSettings(settings);
+ app.start();
+ }
+
+ private void loadRandomMaterials() {
+ for (int i = 0; i < randomMaterials.length; i++) {
+ Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ mat.setBoolean("VertexLighting", true);
+ mat.setBoolean("UseMaterialColors", true);
+ mat.setColor("Ambient", ColorRGBA.Black);
+ mat.setColor("Diffuse", ColorRGBA.White);
+ mat.setColor("Specular", ColorRGBA.White);
+ mat.setFloat("Shininess", 32);
+ mat.setTexture("DiffuseMap", assetManager.loadTexture(textureList[i % textureList.length]));
+ randomMaterials[i] = mat;
+ }
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setDragToRotate(true);
+
+ cam.setLocation(new Vector3f(22.717342f, 18.366547f, 22.043106f));
+ cam.setRotation(new Quaternion(-0.11630201f, 0.8794429f, -0.27703872f, -0.36919326f));
+
+ DirectionalLight dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ rootNode.addLight(dl);
+
+ flyCam.setMoveSpeed(5);
+
+ loadRandomMaterials();
+
+ // Box box = new Box(1,1,1);
+
+ for (int y = -25; y < 25; y++) {
+ for (int x = -25; x < 25; x++) {
+ Material mat = randomMaterials[0]; // randomMaterials[FastMath.nextRandomInt(0, randomMaterials.length - 1)];
+
+ Box box = new Box(1,1,1);
+ Geometry boxClone = new Geometry("box", box);
+ boxClone.setMaterial(mat);
+
+ boxClone.setLocalTranslation(x * .5f, 0, y * .5f);
+ boxClone.setLocalScale(.15f);
+ boxClone.setMaterial(mat);
+ rootNode.attachChild(boxClone);
+ }
+ }
+ }
+}
diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java
deleted file mode 100644
index 8f3c5b156..000000000
--- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java
+++ /dev/null
@@ -1,529 +0,0 @@
-/*
- * Copyright (c) 2009-2012 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.jme3.renderer.jogl;
-
-import com.jme3.renderer.RendererException;
-import com.jme3.texture.Image;
-import com.jme3.texture.Image.Format;
-import com.jme3.texture.image.ColorSpace;
-
-import java.nio.ByteBuffer;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.jogamp.opengl.GL;
-import com.jogamp.opengl.GL2;
-import com.jogamp.opengl.GL2ES2;
-import com.jogamp.opengl.GL2ES3;
-import com.jogamp.opengl.GL2GL3;
-import com.jogamp.opengl.GLContext;
-
-public class TextureUtil {
-
- private static boolean abgrToRgbaConversionEnabled = false;
-
- public static int convertTextureFormat(Format fmt) {
- switch (fmt) {
- case Alpha8:
- return GL.GL_ALPHA;
- case Luminance8Alpha8:
- return GL.GL_LUMINANCE_ALPHA;
- case Luminance8:
- return GL.GL_LUMINANCE;
- case BGR8:
- case RGB8:
- case RGB565:
- return GL.GL_RGB;
- case RGB5A1:
- case RGBA8:
- return GL.GL_RGBA;
- case Depth:
- return GL2ES2.GL_DEPTH_COMPONENT;
- default:
- throw new UnsupportedOperationException("Unrecognized format: " + fmt);
- }
- }
-
- public static class GLImageFormat {
-
- int internalFormat;
- int format;
- int dataType;
- boolean compressed;
-
- public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed) {
- this.internalFormat = internalFormat;
- this.format = format;
- this.dataType = dataType;
- this.compressed = compressed;
- }
- }
-
- private static final GLImageFormat[] formatToGL = new GLImageFormat[Format.values().length];
-
- private static void setFormat(Format format, int glInternalFormat, int glFormat, int glDataType, boolean glCompressed){
- formatToGL[format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, glCompressed);
- }
-
- static {
- // Alpha formats
- setFormat(Format.Alpha8, GL2.GL_ALPHA8, GL.GL_ALPHA, GL.GL_UNSIGNED_BYTE, false);
-
- // Luminance formats
- setFormat(Format.Luminance8, GL2.GL_LUMINANCE8, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.Luminance16F, GL2.GL_LUMINANCE16F, GL.GL_LUMINANCE, GL.GL_HALF_FLOAT, false);
- setFormat(Format.Luminance32F, GL2.GL_LUMINANCE32F, GL.GL_LUMINANCE, GL.GL_FLOAT, false);
-
- // Luminance alpha formats
- setFormat(Format.Luminance8Alpha8, GL2.GL_LUMINANCE8_ALPHA8, GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.Luminance16FAlpha16F, GL2.GL_LUMINANCE_ALPHA16F, GL.GL_LUMINANCE_ALPHA, GL.GL_HALF_FLOAT, false);
-
- // Depth formats
- setFormat(Format.Depth, GL2ES2.GL_DEPTH_COMPONENT, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.Depth16, GL.GL_DEPTH_COMPONENT16, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_SHORT, false);
- setFormat(Format.Depth24, GL.GL_DEPTH_COMPONENT24, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, false);
- setFormat(Format.Depth32, GL.GL_DEPTH_COMPONENT32, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT, false);
- setFormat(Format.Depth32F, GL2GL3.GL_DEPTH_COMPONENT32F, GL2ES2.GL_DEPTH_COMPONENT, GL.GL_FLOAT, false);
-
- // Depth stencil formats
- setFormat(Format.Depth24Stencil8, GL.GL_DEPTH24_STENCIL8, GL.GL_DEPTH_STENCIL, GL.GL_UNSIGNED_INT_24_8, false);
-
- // RGB formats
- setFormat(Format.BGR8, GL.GL_RGB8, GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.ARGB8, GL.GL_RGBA8, GL.GL_BGRA, GL2.GL_UNSIGNED_INT_8_8_8_8_REV, false);
- setFormat(Format.BGRA8, GL.GL_RGBA8, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.RGB8, GL.GL_RGB8, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.RGB16F, GL2ES2.GL_RGB16F, GL.GL_RGB, GL.GL_HALF_FLOAT, false);
- setFormat(Format.RGB32F, GL.GL_RGB32F, GL.GL_RGB, GL.GL_FLOAT, false);
-
- // Special RGB formats
- setFormat(Format.RGB111110F, GL2ES3.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_UNSIGNED_INT_10F_11F_11F_REV, false);
- setFormat(Format.RGB9E5, GL2GL3.GL_RGB9_E5, GL.GL_RGB, GL2GL3.GL_UNSIGNED_INT_5_9_9_9_REV, false);
- setFormat(Format.RGB16F_to_RGB111110F, GL2ES3.GL_R11F_G11F_B10F, GL.GL_RGB, GL.GL_HALF_FLOAT, false);
- setFormat(Format.RGB16F_to_RGB9E5, GL2.GL_RGB9_E5, GL.GL_RGB, GL.GL_HALF_FLOAT, false);
-
- // RGBA formats
- setFormat(Format.ABGR8, GL.GL_RGBA8, GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.RGB5A1, GL.GL_RGB5_A1, GL.GL_RGBA, GL.GL_UNSIGNED_SHORT_5_5_5_1, false);
- setFormat(Format.RGBA8, GL.GL_RGBA8, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false);
- setFormat(Format.RGBA16F, GL2ES2.GL_RGBA16F, GL.GL_RGBA, GL.GL_HALF_FLOAT, false);
- setFormat(Format.RGBA32F, GL.GL_RGBA32F, GL.GL_RGBA, GL.GL_FLOAT, false);
-
- // DXT formats
- setFormat(Format.DXT1, GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, true);
- setFormat(Format.DXT1A, GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true);
- setFormat(Format.DXT3, GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true);
- setFormat(Format.DXT5, GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true);
- }
-
- //sRGB formats
- private static final GLImageFormat sRGB_RGB8 = new GLImageFormat(GL2.GL_SRGB8,GL.GL_RGB, GL.GL_UNSIGNED_BYTE, false);
- private static final GLImageFormat sRGB_RGBA8 = new GLImageFormat(GL.GL_SRGB8_ALPHA8,GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false);
- private static final GLImageFormat sRGB_Luminance8 = new GLImageFormat(GL2.GL_SLUMINANCE8,GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, false);
- private static final GLImageFormat sRGB_LuminanceAlpha8 = new GLImageFormat(GL2.GL_SLUMINANCE8_ALPHA8,GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, false);
- private static final GLImageFormat sRGB_BGR8 = new GLImageFormat(GL2.GL_SRGB8, GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, false);
- private static final GLImageFormat sRGB_ABGR8 = new GLImageFormat(GL2.GL_SRGB8_ALPHA8, GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, false);
-
- //FIXME cannot find GL_COMPRESSED_RGB_S3TC_DXT1,GL_COMPRESSED_RGBA_S3TC_DXT1,GL_COMPRESSED_RGB_S3TC_DXT3,GL_COMPRESSED_RGB_S3TC_DXT5 in JOGL used constants
- //GL_COMPRESSED_RGB_S3TC_DXT1 = 33776;
- //GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 33777;
- //GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 33778;
- //GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 33779;
- private static final GLImageFormat sRGB_DXT1 = new GLImageFormat(33776, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, true);
- private static final GLImageFormat sRGB_DXT1A = new GLImageFormat( 33777, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true);
- private static final GLImageFormat sRGB_DXT3 = new GLImageFormat(33778, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true);
- private static final GLImageFormat sRGB_DXT5 = new GLImageFormat( 33779, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, true);
-
-
- public static GLImageFormat getImageFormat(Format fmt, boolean isSrgb){
- GL gl = GLContext.getCurrentGL();
- switch (fmt){
- case ABGR8:
- if (!gl.isExtensionAvailable("GL_EXT_abgr") && !abgrToRgbaConversionEnabled) {
- setFormat(Format.ABGR8, GL.GL_RGBA, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, false);
- abgrToRgbaConversionEnabled = true;
- }
- break;
- case BGR8:
- if (!gl.isExtensionAvailable("GL_VERSION_1_2") && !gl.isExtensionAvailable("EXT_bgra")){
- return null;
- }
- break;
- case DXT1:
- case DXT1A:
- case DXT3:
- case DXT5:
- if (!gl.isExtensionAvailable("GL_EXT_texture_compression_s3tc")) {
- return null;
- }
- break;
- case Depth:
- case Depth16:
- case Depth24:
- case Depth32:
- if (!gl.isExtensionAvailable("GL_VERSION_1_4") && !gl.isExtensionAvailable("ARB_depth_texture")){
- return null;
- }
- break;
- case Depth24Stencil8:
- if (!gl.isExtensionAvailable("GL_VERSION_3_0")){
- return null;
- }
- break;
- case Luminance16F:
- case Luminance16FAlpha16F:
- case Luminance32F:
- if (!gl.isExtensionAvailable("GL_ARB_texture_float")){
- return null;
- }
- break;
- case RGB16F:
- case RGB32F:
- case RGBA16F:
- case RGBA32F:
- if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_ARB_texture_float")){
- return null;
- }
- break;
- case Depth32F:
- if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_NV_depth_buffer_float")){
- return null;
- }
- break;
- case RGB9E5:
- case RGB16F_to_RGB9E5:
- if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_EXT_texture_shared_exponent")){
- return null;
- }
- break;
- case RGB111110F:
- case RGB16F_to_RGB111110F:
- if (!gl.isExtensionAvailable("GL_VERSION_3_0") && !gl.isExtensionAvailable("GL_EXT_packed_float")){
- return null;
- }
- break;
- }
-
- if(isSrgb){
- return getSrgbFormat(fmt);
- }
- return formatToGL[fmt.ordinal()];
- }
-
- public static GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) {
- GLImageFormat glFmt = getImageFormat(fmt, isSrgb);
- if (glFmt == null) {
- throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware.");
- }
- return glFmt;
- }
-
- private static GLImageFormat getSrgbFormat(Format fmt){
- switch (fmt){
- case RGB8 : return sRGB_RGB8;
- case RGBA8 : return sRGB_RGBA8;
- case BGR8 : return sRGB_BGR8;
- case ABGR8 : return sRGB_ABGR8;
- case Luminance8 : return sRGB_Luminance8;
- case Luminance8Alpha8 : return sRGB_LuminanceAlpha8;
- case DXT1 : return sRGB_DXT1;
- case DXT1A : return sRGB_DXT1A;
- case DXT3 : return sRGB_DXT3;
- case DXT5 : return sRGB_DXT5;
- default : Logger.getLogger(TextureUtil.class.getName()).log(Level.WARNING, "Format {0} has no sRGB equivalent, using linear format.", fmt.toString());
- return formatToGL[fmt.ordinal()];
- }
- }
-
- public static void uploadTexture(Image image,
- int target,
- int index,
- int border,
- boolean linearizeSrgb){
- GL gl = GLContext.getCurrentGL();
- Image.Format fmt = image.getFormat();
- GLImageFormat glFmt = getImageFormatWithError(fmt, image.getColorSpace() == ColorSpace.sRGB && linearizeSrgb);
-
- ByteBuffer data;
- if (index >= 0 && image.getData() != null && image.getData().size() > 0){
- data = image.getData(index);
- }else{
- data = null;
- }
-
- int width = image.getWidth();
- int height = image.getHeight();
- int depth = image.getDepth();
-
- if (data != null) {
- if (abgrToRgbaConversionEnabled) {
- convertABGRtoRGBA(data);
- }
- gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
- }
-
- int[] mipSizes = image.getMipMapSizes();
- int pos = 0;
- // TODO: Remove unneccessary allocation
- if (mipSizes == null){
- if (data != null)
- mipSizes = new int[]{ data.capacity() };
- else
- mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 };
- }
-
- boolean subtex = false;
- int samples = image.getMultiSamples();
-
- for (int i = 0; i < mipSizes.length; i++){
- int mipWidth = Math.max(1, width >> i);
- int mipHeight = Math.max(1, height >> i);
- int mipDepth = Math.max(1, depth >> i);
-
- if (data != null){
- data.position(pos);
- data.limit(pos + mipSizes[i]);
- }
-
- if (glFmt.compressed && data != null){
- if (target == GL2ES2.GL_TEXTURE_3D){
- gl.getGL2ES2().glCompressedTexImage3D(target,
- i,
- glFmt.internalFormat,
- mipWidth,
- mipHeight,
- mipDepth,
- data.remaining(),
- border,
- data);
- }else{
- //all other targets use 2D: array, cubemap, 2d
- gl.glCompressedTexImage2D(target,
- i,
- glFmt.internalFormat,
- mipWidth,
- mipHeight,
- data.remaining(),
- border,
- data);
- }
- }else{
- if (target == GL2ES2.GL_TEXTURE_3D){
- gl.getGL2ES2().glTexImage3D(target,
- i,
- glFmt.internalFormat,
- mipWidth,
- mipHeight,
- mipDepth,
- border,
- glFmt.format,
- glFmt.dataType,
- data);
- }else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY){
- // prepare data for 2D array
- // or upload slice
- if (index == -1){
- gl.getGL2ES2().glTexImage3D(target,
- 0,
- glFmt.internalFormat,
- mipWidth,
- mipHeight,
- image.getData().size(), //# of slices
- border,
- glFmt.format,
- glFmt.dataType,
- data);
- }else{
- gl.getGL2ES2().glTexSubImage3D(target,
- i, // level
- 0, // xoffset
- 0, // yoffset
- index, // zoffset
- width, // width
- height, // height
- 1, // depth
- glFmt.format,
- glFmt.dataType,
- data);
- }
- }else{
- if (subtex){
- if (samples > 1){
- throw new IllegalStateException("Cannot update multisample textures");
- }
-
- gl.glTexSubImage2D(target,
- i,
- 0, 0,
- mipWidth, mipHeight,
- glFmt.format,
- glFmt.dataType,
- data);
- }else{
- if (samples > 1){
- if (gl.isGL2GL3()) {
- gl.getGL3().glTexImage2DMultisample(target,
- samples,
- glFmt.internalFormat,
- mipWidth,
- mipHeight,
- true);
- }
- } else {
- gl.glTexImage2D(target,
- i,
- glFmt.internalFormat,
- mipWidth,
- mipHeight,
- border,
- glFmt.format,
- glFmt.dataType,
- data);
- }
- }
- }
- }
-
- pos += mipSizes[i];
- }
- }
-
- private static void convertABGRtoRGBA(ByteBuffer buffer) {
-
- for (int i = 0; i < buffer.capacity(); i++) {
-
- int a = buffer.get(i++);
- int b = buffer.get(i++);
- int g = buffer.get(i++);
- int r = buffer.get(i);
-
- buffer.put(i - 3, (byte) r);
- buffer.put(i - 2, (byte) g);
- buffer.put(i - 1, (byte) b);
- buffer.put(i, (byte) a);
-
- }
-
- }
-
- /**
- * 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 image,
- int target,
- int index,
- int x,
- int y,
- boolean linearizeSrgb) {
- GL gl = GLContext.getCurrentGL();
- Image.Format fmt = image.getFormat();
- GLImageFormat glFmt = getImageFormatWithError(fmt, image.getColorSpace() == ColorSpace.sRGB && linearizeSrgb);
-
- ByteBuffer data = null;
- if (index >= 0 && image.getData() != null && image.getData().size() > 0) {
- data = image.getData(index);
- }
-
- int width = image.getWidth();
- int height = image.getHeight();
- int depth = image.getDepth();
-
- if (data != null) {
- gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
- }
-
- int[] mipSizes = image.getMipMapSizes();
- int pos = 0;
-
- // TODO: Remove unneccessary allocation
- if (mipSizes == null){
- if (data != null) {
- mipSizes = new int[]{ data.capacity() };
- } else {
- mipSizes = new int[]{ width * height * fmt.getBitsPerPixel() / 8 };
- }
- }
-
- int samples = image.getMultiSamples();
-
- for (int i = 0; i < mipSizes.length; i++){
- int mipWidth = Math.max(1, width >> i);
- int mipHeight = Math.max(1, height >> i);
- int mipDepth = Math.max(1, depth >> i);
-
- if (data != null){
- data.position(pos);
- data.limit(pos + mipSizes[i]);
- }
-
- // to remove the cumbersome if/then/else stuff below we'll update the pos right here and use continue after each
- // gl*Image call in an attempt to unclutter things a bit
- pos += mipSizes[i];
-
- int glFmtInternal = glFmt.internalFormat;
- int glFmtFormat = glFmt.format;
- int glFmtDataType = glFmt.dataType;
-
- if (glFmt.compressed && data != null){
- if (target == GL2ES2.GL_TEXTURE_3D){
- gl.getGL2ES2().glCompressedTexSubImage3D(target, i, x, y, index, mipWidth, mipHeight, mipDepth, glFmtInternal, data.limit(), data);
- continue;
- }
-
- // all other targets use 2D: array, cubemap, 2d
- gl.getGL2ES2().glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, glFmtInternal, data.limit(), data);
- continue;
- }
-
- if (target == GL2ES2.GL_TEXTURE_3D){
- gl.getGL2ES2().glTexSubImage3D(target, i, x, y, index, mipWidth, mipHeight, mipDepth, glFmtFormat, glFmtDataType, data);
- continue;
- }
-
- if (samples > 1){
- throw new IllegalStateException("Cannot update multisample textures");
- }
-
- gl.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, glFmtFormat, glFmtDataType, data);
- continue;
- }
- }
-}
diff --git a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
index 497fa8edb..936cab616 100644
--- a/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
+++ b/jme3-jogl/src/main/java/com/jme3/system/jogl/JoglContext.java
@@ -54,6 +54,7 @@ import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext;
import com.jme3.system.NanoTimer;
import com.jme3.system.NativeLibraryLoader;
+import com.jme3.system.NullRenderer;
import com.jme3.system.SystemListener;
import com.jme3.system.Timer;
@@ -69,9 +70,9 @@ import com.jogamp.opengl.GLContext;
public abstract class JoglContext implements JmeContext {
private static final Logger logger = Logger.getLogger(JoglContext.class.getName());
-
+
protected static final String THREAD_NAME = "jME3 Main";
-
+
protected AtomicBoolean created = new AtomicBoolean(false);
protected AtomicBoolean renderable = new AtomicBoolean(false);
protected final Object createdLock = new Object();
@@ -91,7 +92,7 @@ public abstract class JoglContext implements JmeContext {
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
}
}
-
+
@Override
public void setSystemListener(SystemListener listener){
this.listener = listener;
@@ -101,7 +102,7 @@ public abstract class JoglContext implements JmeContext {
public void setSettings(AppSettings settings) {
this.settings.copyFrom(settings);
}
-
+
@Override
public boolean isRenderable(){
return renderable.get();
@@ -160,48 +161,48 @@ public abstract class JoglContext implements JmeContext {
}
}
}
-
+
protected void initContextFirstTime(){
if (GLContext.getCurrent().getGLVersionNumber().getMajor() < 2) {
- throw new RendererException("OpenGL 2.0 or higher is " +
+ throw new RendererException("OpenGL 2.0 or higher is " +
"required for jMonkeyEngine");
}
-
+
if (settings.getRenderer().startsWith("JOGL")) {
com.jme3.renderer.opengl.GL gl = new JoglGL();
GLExt glext = new JoglGLExt();
GLFbo glfbo = new JoglGLFbo();
-
+
if (settings.getBoolean("GraphicsDebug")) {
gl = new GLDebugDesktop(gl, glext, glfbo);
glext = (GLExt) gl;
glfbo = (GLFbo) gl;
}
-
+
if (settings.getBoolean("GraphicsTiming")) {
GLTimingState timingState = new GLTimingState();
gl = (com.jme3.renderer.opengl.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 = (com.jme3.renderer.opengl.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, glext, glfbo);
renderer.initialize();
} else {
throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer());
}
-
+
if (GLContext.getCurrentGL().isExtensionAvailable("GL_ARB_debug_output") && settings.getBoolean("GraphicsDebug")) {
GLContext.getCurrent().enableGLDebugMessage(true);
GLContext.getCurrent().addGLDebugListener(new JoglGLDebugOutputHandler());
}
-
+
renderer.setMainFrameBufferSrgb(settings.getGammaCorrection());
renderer.setLinearizeSrgbImages(settings.getGammaCorrection());
@@ -241,7 +242,7 @@ public abstract class JoglContext implements JmeContext {
createdLock.notifyAll();
}
}
-
+
protected int determineMaxSamples(int requestedSamples) {
GL gl = GLContext.getCurrentGL();
if (gl.hasFullFBOSupport()) {
@@ -257,7 +258,7 @@ public abstract class JoglContext implements JmeContext {
}
}
}
-
+
protected int getNumSamplesToUse() {
int samples = 0;
if (settings.getSamples() > 1){
@@ -268,7 +269,7 @@ public abstract class JoglContext implements JmeContext {
"Couldn''t satisfy antialiasing samples requirement: x{0}. "
+ "Video hardware only supports: x{1}",
new Object[]{samples, supportedSamples});
-
+
samples = supportedSamples;
}
}