From 937d97b8d744c90207995dd9ace846be62141771 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Fri, 21 Aug 2015 20:32:27 -0400 Subject: [PATCH 01/15] GLRenderer: initial VAO support (still buggy) --- .../com/jme3/renderer/opengl/GLRenderer.java | 520 ++++++++++-------- 1 file changed, 303 insertions(+), 217 deletions(-) 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 0aa0d693f..f6f787a8a 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 @@ -1816,7 +1816,7 @@ public class GLRenderer implements Renderer { } @SuppressWarnings("fallthrough") - private void setupTextureParams(int unit, Texture tex) { + private void setupTextureParams(Texture tex) { Image image = tex.getImage(); int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1); @@ -1829,23 +1829,32 @@ public class GLRenderer implements Renderer { // filter things if (image.getLastTextureState().magFilter != tex.getMagFilter()) { int magFilter = convertMagFilter(tex.getMagFilter()); - bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter); image.getLastTextureState().magFilter = tex.getMagFilter(); } if (image.getLastTextureState().minFilter != tex.getMinFilter()) { int minFilter = convertMinFilter(tex.getMinFilter(), haveMips); - bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter); image.getLastTextureState().minFilter = tex.getMinFilter(); } - if (caps.contains(Caps.TextureFilterAnisotropic) - && image.getLastTextureState().anisoFilter != tex.getAnisotropicFilter()) { - bindTextureAndUnit(target, image, unit); - gl.glTexParameterf(target, - GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, - tex.getAnisotropicFilter()); - image.getLastTextureState().anisoFilter = tex.getAnisotropicFilter(); + if (caps.contains(Caps.SeamlessCubemap) && tex.getType() == Texture.Type.CubeMap) { + if (haveMips && !context.seamlessCubemap) { + // We can enable seamless cubemap filtering. + gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS); + context.seamlessCubemap = true; + } else if (!haveMips && context.seamlessCubemap) { + // For skyboxes (no mipmaps), disable seamless cubemap filtering. + gl.glDisable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS); + context.seamlessCubemap = false; + } + } + + if (tex.getAnisotropicFilter() > 1) { + if (caps.contains(Caps.TextureFilterAnisotropic)) { + gl.glTexParameterf(target, + GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, + tex.getAnisotropicFilter()); + } } // repeat modes @@ -1853,7 +1862,6 @@ public class GLRenderer implements Renderer { case ThreeDimensional: case CubeMap: // cubemaps use 3D coords if (gl2 != null && image.getLastTextureState().rWrap != tex.getWrap(WrapAxis.R)) { - bindTextureAndUnit(target, image, unit); gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); image.getLastTextureState().rWrap = tex.getWrap(WrapAxis.R); } @@ -1861,12 +1869,10 @@ public class GLRenderer implements Renderer { case TwoDimensional: case TwoDimensionalArray: if (image.getLastTextureState().tWrap != tex.getWrap(WrapAxis.T)) { - bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); image.getLastTextureState().tWrap = tex.getWrap(WrapAxis.T); } if (image.getLastTextureState().sWrap != tex.getWrap(WrapAxis.S)) { - bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); image.getLastTextureState().sWrap = tex.getWrap(WrapAxis.S); } @@ -1875,10 +1881,9 @@ public class GLRenderer implements Renderer { throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); } - if (tex.isNeedCompareModeUpdate() && gl2 != null) { + if(tex.isNeedCompareModeUpdate() && gl2 != null){ // R to Texture compare mode if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { - bindTextureAndUnit(target, image, unit); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { @@ -1886,16 +1891,12 @@ public class GLRenderer implements Renderer { } else { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); } - } else { - bindTextureAndUnit(target, image, unit); + }else{ //restoring default value gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); } tex.compareModeUpdated(); } - - // If at this point we didn't bind the texture, bind it now - bindTextureOnly(target, image, unit); } /** @@ -1953,50 +1954,6 @@ public 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. - */ - private void bindTextureAndUnit(int target, Image img, int unit) { - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - if (context.boundTextures[unit] != img) { - gl.glBindTexture(target, img.getId()); - context.boundTextures[unit] = img; - statistics.onTextureUse(img, true); - } else { - 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. - */ - private void bindTextureOnly(int target, Image img, int unit) { - if (context.boundTextures[unit] != img) { - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - gl.glBindTexture(target, img.getId()); - context.boundTextures[unit] = img; - statistics.onTextureUse(img, true); - } else { - statistics.onTextureUse(img, false); - } - } - /** * Uploads the given image to the GL driver. * @@ -2018,9 +1975,19 @@ public class GLRenderer implements Renderer { statistics.onNewTexture(); } - // bind texture + // bind texture int target = convertTextureType(type, img.getMultiSamples(), -1); - bindTextureAndUnit(target, img, unit); + if (context.boundTextures[unit] != img) { + if (context.boundTextureUnit != unit) { + gl.glActiveTexture(GL.GL_TEXTURE0 + unit); + context.boundTextureUnit = unit; + } + + gl.glBindTexture(target, texId); + context.boundTextures[unit] = img; + + statistics.onTextureUse(img, true); + } if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { // Image does not have mipmaps, but they are required. @@ -2129,7 +2096,6 @@ public class GLRenderer implements Renderer { img.clearUpdateNeeded(); } - @Override public void setTexture(int unit, Texture tex) { Image image = tex.getImage(); if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { @@ -2156,7 +2122,24 @@ public class GLRenderer implements Renderer { int texId = image.getId(); assert texId != -1; - setupTextureParams(unit, tex); + Image[] textures = context.boundTextures; + + int type = convertTextureType(tex.getType(), image.getMultiSamples(), -1); + if (textures[unit] != image) { + if (context.boundTextureUnit != unit) { + gl.glActiveTexture(GL.GL_TEXTURE0 + unit); + context.boundTextureUnit = unit; + } + + gl.glBindTexture(type, texId); + textures[unit] = image; + + statistics.onTextureUse(image, true); + } else { + statistics.onTextureUse(image, false); + } + + setupTextureParams(tex); } public void modifyTexture(Texture tex, Image pixels, int x, int y) { @@ -2305,33 +2288,38 @@ public 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)) { @@ -2355,11 +2343,11 @@ public 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; @@ -2372,12 +2360,12 @@ public 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. @@ -2388,17 +2376,17 @@ public 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()); @@ -2411,6 +2399,92 @@ public 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); } @@ -2463,57 +2537,19 @@ public class GLRenderer implements Renderer { int vertCount = mesh.getVertexCount(); boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); - 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 (useInstancing) { - glext.glDrawElementsInstancedARB(elMode, - elementLength, - fmt, - curOffset, - count); - } else { - gl.glDrawRangeElements(elMode, - 0, - vertCount, - elementLength, - fmt, - curOffset); - } - - curOffset += elementLength * elSize; - } + if (useInstancing) { + glext.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()), + indexBuf.getData().limit(), + convertFormat(indexBuf.getFormat()), + 0, + count); } else { - if (useInstancing) { - 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); } } @@ -2542,30 +2578,19 @@ public class GLRenderer implements Renderer { throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode); } } - - 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()) { if (vb.getBufferType() == Type.InterleavedData || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers @@ -2582,81 +2607,124 @@ public 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 - } - - if (context.boundVertexArray != mesh.getId()) { - gl3.glBindVertexArray(mesh.getId()); - context.boundVertexArray = mesh.getId(); + + private void setupVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) { + VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); + if (instanceData != null) { + for (VertexBuffer vb : instanceData) { + setVertexAttribVAO(vb, null); + } } + + 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(); + + mesh.clearUpdateNeeded(); } - 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); - + 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); + + VertexBuffer indices = getIndexBuffer(mesh, lod); + if (mesh.isUpdateNeeded()) { + setupVertexBuffers(mesh, instanceData); + 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()); } + } - clearVertexAttribs(); - + private void renderMeshLegacy(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { + setupVertexBuffersLegacy(mesh, instanceData); + VertexBuffer indices = getIndexBuffer(mesh, lod); if (indices != null) { drawTriangleList(indices, mesh, count); } else { drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); } + clearVertexAttribs(); } public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { @@ -2673,14 +2741,32 @@ public 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) { // Gamma correction if (!caps.contains(Caps.Srgb) && enableSrgb) { From d2f38f8adb697441deeb01ff39349af52a1b2339 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Fri, 21 Aug 2015 20:34:34 -0400 Subject: [PATCH 02/15] GLRenderer: added fast uniforms - still need to fix Uniform.clear() --- .../com/jme3/renderer/opengl/GLRenderer.java | 14 ++-- .../main/java/com/jme3/shader/Uniform.java | 64 +++++++++++++------ 2 files changed, 52 insertions(+), 26 deletions(-) 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 f6f787a8a..ae826b1c8 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 @@ -957,12 +957,12 @@ public 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; @@ -971,23 +971,23 @@ public 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: 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..521b10d38 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,9 +323,9 @@ public class Uniform extends ShaderVariable { break; } - if (multiData != null) { - this.value = multiData; - } +// if (multiData != null) { +// this.value = multiData; +// } varType = type; updateNeeded = true; From 7b64e91681d70fea976040910ef5445cc28f35ef Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Fri, 21 Aug 2015 21:21:31 -0400 Subject: [PATCH 03/15] GLRenderer: remaining portion of VAO support --- .../java/com/jme3/font/BitmapTextPage.java | 5 + .../main/java/com/jme3/renderer/Renderer.java | 6 + .../src/main/java/com/jme3/scene/Mesh.java | 199 ++++++------------ .../java/com/jme3/scene/VertexBuffer.java | 17 +- .../src/main/java/com/jme3/shader/Shader.java | 10 + .../java/com/jme3/system/NullRenderer.java | 3 + .../main/java/com/jme3/util/NativeObject.java | 3 +- 7 files changed, 108 insertions(+), 135 deletions(-) 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 1edac967c..d0d2e6969 100644 --- a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java +++ b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java @@ -137,6 +137,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/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index a0f8e1fe6..fbe5d7794 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; } /** @@ -475,40 +455,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 +845,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 +1038,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 +1311,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 +1342,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 +1377,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/shader/Shader.java b/jme3-core/src/main/java/com/jme3/shader/Shader.java index eb084f178..cfe915ad8 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Shader.java +++ b/jme3-core/src/main/java/com/jme3/shader/Shader.java @@ -274,6 +274,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/system/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java index f2e029af4..7ac0b3495 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. From 81a76fdf69e2995e020cbc9c38714be83a201ba5 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Fri, 21 Aug 2015 21:58:42 -0400 Subject: [PATCH 04/15] Mesh: preliminary work to use ubyte weights for hardware skinning --- .../src/main/java/com/jme3/scene/Mesh.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 fbe5d7794..a38bf0447 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -353,12 +353,17 @@ public class Mesh extends NativeObject implements Savable { // 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 From c72b036c9f9c9e0d6f10624189bc92adedddf7a7 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Fri, 21 Aug 2015 22:32:36 -0400 Subject: [PATCH 05/15] VirtualIndexBuffer: fix compile error --- .../src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(); } } From 2e9a9f9f9ef09819fe6320e9651f1e8e979163af Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 22 Aug 2015 13:48:20 -0400 Subject: [PATCH 06/15] Add test for VAO --- .../jme3test/stress/TestUniqueGeometries.java | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java 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..d1a14be8e --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java @@ -0,0 +1,139 @@ +/* + * 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 TestObjects 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) { + TestObjects app = new TestObjects(); + AppSettings settings = new AppSettings(true); + settings.putBoolean("GraphicsTrace", false); + settings.putBoolean("GraphicsTiming", false); + 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() { + 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[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); + } + } + } +} From 7b147171bf8b1857efc3be903a90e321e7b2e094 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 22 Aug 2015 13:49:12 -0400 Subject: [PATCH 07/15] VAO: changes to test --- .../java/jme3test/stress/TestUniqueGeometries.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java b/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java index d1a14be8e..3fbf34117 100644 --- a/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java +++ b/jme3-examples/src/main/java/jme3test/stress/TestUniqueGeometries.java @@ -45,7 +45,7 @@ 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 TestObjects extends SimpleApplication { +public class TestUniqueGeometries extends SimpleApplication { private Material[] randomMaterials = new Material[50]; @@ -84,10 +84,10 @@ public class TestObjects extends SimpleApplication { }; public static void main(String[] args) { - TestObjects app = new TestObjects(); + TestUniqueGeometries app = new TestUniqueGeometries(); AppSettings settings = new AppSettings(true); settings.putBoolean("GraphicsTrace", false); - settings.putBoolean("GraphicsTiming", false); + settings.putBoolean("GraphicsTiming", true); app.setSettings(settings); app.start(); } @@ -108,6 +108,8 @@ public class TestObjects extends SimpleApplication { @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)); @@ -119,11 +121,11 @@ public class TestObjects extends SimpleApplication { loadRandomMaterials(); -// Box box = new Box(1,1,1); + // Box box = new Box(1,1,1); for (int y = -25; y < 25; y++) { for (int x = -25; x < 25; x++) { - Material mat = randomMaterials[FastMath.nextRandomInt(0, randomMaterials.length - 1)]; + Material mat = randomMaterials[0]; // randomMaterials[FastMath.nextRandomInt(0, randomMaterials.length - 1)]; Box box = new Box(1,1,1); Geometry boxClone = new Geometry("box", box); From 12c001addcc802a65ed3a622ad27b47e801253dd Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 5 Sep 2015 17:31:59 -0400 Subject: [PATCH 08/15] Uniform: fix crash when using vector4array --- .../main/java/com/jme3/shader/Uniform.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) 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 521b10d38..9580d8b72 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Uniform.java +++ b/jme3-core/src/main/java/com/jme3/shader/Uniform.java @@ -332,30 +332,29 @@ public class Uniform extends ShaderVariable { } 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; } From fc680ea121408d08866a34fd92b1e871e1c26932 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 5 Sep 2015 17:33:22 -0400 Subject: [PATCH 09/15] GLRenderer: use luminance instead of intensity in compare R to texture --- jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java | 1 - 1 file changed, 1 deletion(-) 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 ae826b1c8..d8f523f61 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 @@ -1885,7 +1885,6 @@ public class GLRenderer implements Renderer { // R to Texture compare mode if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); - gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); } else { From 3a00aff886184e49b8754b2087a0ea276c2e1ef4 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 5 Sep 2015 17:33:59 -0400 Subject: [PATCH 10/15] GLRenderer: clear VBO bind state after bounding VAO --- .../src/main/java/com/jme3/renderer/opengl/GLRenderer.java | 4 ++++ 1 file changed, 4 insertions(+) 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 d8f523f61..8b6a307be 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 @@ -2689,6 +2689,10 @@ public class GLRenderer implements Renderer { 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); From 618c8d02eb2335c17f6aca9d2556bc7ef7797c2b Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 12 Sep 2015 17:50:18 -0400 Subject: [PATCH 11/15] JOGL: delete old / broken renderer --- .../com/jme3/renderer/jogl/JoglRenderer.java | 2699 ----------------- .../com/jme3/renderer/jogl/TextureUtil.java | 529 ---- 2 files changed, 3228 deletions(-) delete mode 100644 jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java delete mode 100644 jme3-jogl/src/main/java/com/jme3/renderer/jogl/TextureUtil.java diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java deleted file mode 100644 index 111719265..000000000 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java +++ /dev/null @@ -1,2699 +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.light.LightList; -import com.jme3.material.RenderState; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Matrix4f; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.math.Vector4f; -import com.jme3.renderer.Caps; -import com.jme3.renderer.IDList; -import com.jme3.renderer.RenderContext; -import com.jme3.renderer.Renderer; -import com.jme3.renderer.RendererException; -import com.jme3.renderer.Statistics; -import com.jme3.scene.Mesh; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer; -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.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.Buffer; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.util.EnumSet; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import com.jogamp.nativewindow.NativeWindowFactory; -import com.jogamp.opengl.GL; -import com.jogamp.opengl.GL2; -import com.jogamp.opengl.GL2ES1; -import com.jogamp.opengl.GL2ES2; -import com.jogamp.opengl.GL2ES3; -import com.jogamp.opengl.GL2GL3; -import com.jogamp.opengl.GL3; -import com.jogamp.opengl.GLContext; -import jme3tools.converters.MipMapGenerator; -import jme3tools.shader.ShaderDebug; - -public class JoglRenderer implements Renderer { - - private static final Logger logger = Logger.getLogger(JoglRenderer.class.getName()); - private static final boolean VALIDATE_SHADER = false; - private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250); - private final StringBuilder stringBuf = new StringBuilder(250); - private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1); - private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16); - protected FloatBuffer fb16 = BufferUtils.createFloatBuffer(16); - private RenderContext context = new RenderContext(); - private NativeObjectManager objManager = new NativeObjectManager(); - private EnumSet caps = EnumSet.noneOf(Caps.class); - //current state - private Shader boundShader; - private int initialDrawBuf, initialReadBuf; - private int glslVer; - private int vertexTextureUnits; - private int fragTextureUnits; - private int vertexUniforms; - private int fragUniforms; - private int vertexAttribs; - private int maxFBOSamples; - private int maxFBOAttachs; - private int maxMRTFBOAttachs; - private int maxRBSize; - private int maxTexSize; - private int maxCubeTexSize; - private int maxVertCount; - private int maxTriCount; - private int maxColorTexSamples; - private int maxDepthTexSamples; - private FrameBuffer lastFb = null; - private FrameBuffer mainFbOverride = null; - private final Statistics statistics = new Statistics(); - private int vpX, vpY, vpW, vpH; - private int clipX, clipY, clipW, clipH; - private boolean linearizeSrgbImages; - - public JoglRenderer() { - } - - 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(); - } - - @Override - public Statistics getStatistics() { - return statistics; - } - - @Override - public EnumSet getCaps() { - return caps; - } - - public void initialize() { - GL gl = GLContext.getCurrentGL(); - //logger.log(Level.FINE, "Vendor: {0}", gl.glGetString(GL.GL_VENDOR)); - //logger.log(Level.FINE, "Renderer: {0}", gl.glGetString(GL.GL_RENDERER)); - //logger.log(Level.FINE, "Version: {0}", gl.glGetString(GL.GL_VERSION)); - if (gl.isExtensionAvailable("GL_VERSION_2_0")) { - caps.add(Caps.OpenGL20); - if (gl.isExtensionAvailable("GL_VERSION_2_1")) { - caps.add(Caps.OpenGL21); - if (gl.isExtensionAvailable("GL_VERSION_3_0")) { - caps.add(Caps.OpenGL30); - if (gl.isExtensionAvailable("GL_VERSION_3_1")) { - caps.add(Caps.OpenGL31); - if (gl.isExtensionAvailable("GL_VERSION_3_2")) { - caps.add(Caps.OpenGL32); - } - } - } - } - } - - //workaround, always assume we support GLSL100 - //some cards just don't report this correctly - caps.add(Caps.GLSL100); - - String versionStr = null; - if (caps.contains(Caps.OpenGL20) || gl.isGL2ES2()) { - versionStr = gl.glGetString(GL2ES2.GL_SHADING_LANGUAGE_VERSION); - } - if (versionStr == null || versionStr.equals("")) { - glslVer = -1; - throw new UnsupportedOperationException("GLSL and OpenGL2 is " + - "required for the JOGL " + - "renderer!"); - } - - // Fix issue in TestRenderToMemory when GL_FRONT is the main - // buffer being used. - gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, intBuf1); - initialDrawBuf = intBuf1.get(0); - gl.glGetIntegerv(GL2GL3.GL_READ_BUFFER, intBuf1); - initialReadBuf = intBuf1.get(0); - - // 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; - - int spaceIdx = versionStr.indexOf(" "); - if (spaceIdx >= 1) { - versionStr = versionStr.substring(0, spaceIdx); - } - - try { - float version = Float.parseFloat(versionStr); - glslVer = (int) (version * 100); - } catch (NumberFormatException e) { - // the parsing fails on Raspberry Pi - if (NativeWindowFactory.getNativeWindowType(false).equals(NativeWindowFactory.TYPE_BCM_VC_IV)) { - logger.warning("Failed parsing GLSL version assuming it's v1.00"); - glslVer = 100; - } - } - - switch (glslVer) { - default: - if (glslVer < 400) { - break; - } - - // so that future OpenGL revisions wont break jme3 - - // fall through intentional - case 400: - case 330: - case 150: - caps.add(Caps.GLSL150); - case 140: - caps.add(Caps.GLSL140); - case 130: - caps.add(Caps.GLSL130); - case 120: - caps.add(Caps.GLSL120); - case 110: - caps.add(Caps.GLSL110); - case 100: - caps.add(Caps.GLSL100); - break; - } - - if (!caps.contains(Caps.GLSL100)) { - logger.log(Level.WARNING, "Force-adding GLSL100 support, since OpenGL2 is supported."); - caps.add(Caps.GLSL100); - } - - gl.glGetIntegerv(GL2ES2.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, intBuf16); - vertexTextureUnits = intBuf16.get(0); - logger.log(Level.FINER, "VTF Units: {0}", vertexTextureUnits); - if (vertexTextureUnits > 0) { - caps.add(Caps.VertexTextureFetch); - } - - gl.glGetIntegerv(GL2ES2.GL_MAX_TEXTURE_IMAGE_UNITS, intBuf16); - fragTextureUnits = intBuf16.get(0); - logger.log(Level.FINER, "Texture Units: {0}", fragTextureUnits); - - gl.glGetIntegerv(GL2GL3.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16); - vertexUniforms = intBuf16.get(0); - logger.log(Level.FINER, "Vertex Uniforms: {0}", vertexUniforms); - - gl.glGetIntegerv(GL2GL3.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, intBuf16); - fragUniforms = intBuf16.get(0); - logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms); - - gl.glGetIntegerv(GL2ES2.GL_MAX_VERTEX_ATTRIBS, intBuf16); - vertexAttribs = intBuf16.get(0); - logger.log(Level.FINER, "Vertex Attributes: {0}", vertexAttribs); - - gl.glGetIntegerv(GL2GL3.GL_MAX_VARYING_FLOATS, intBuf16); - int varyingFloats = intBuf16.get(0); - logger.log(Level.FINER, "Varying Floats: {0}", varyingFloats); - - gl.glGetIntegerv(GL.GL_SUBPIXEL_BITS, intBuf16); - int subpixelBits = intBuf16.get(0); - logger.log(Level.FINER, "Subpixel Bits: {0}", subpixelBits); - - gl.glGetIntegerv(GL2GL3.GL_MAX_ELEMENTS_VERTICES, intBuf16); - maxVertCount = intBuf16.get(0); - logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount); - - gl.glGetIntegerv(GL2GL3.GL_MAX_ELEMENTS_INDICES, intBuf16); - maxTriCount = intBuf16.get(0); - logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount); - - gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, intBuf16); - maxTexSize = intBuf16.get(0); - logger.log(Level.FINER, "Maximum Texture Resolution: {0}", maxTexSize); - - gl.glGetIntegerv(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE, intBuf16); - maxCubeTexSize = intBuf16.get(0); - logger.log(Level.FINER, "Maximum CubeMap Resolution: {0}", maxCubeTexSize); - - if (gl.isExtensionAvailable("GL_ARB_color_buffer_float")) { - // XXX: Require both 16 and 32 bit float support for FloatColorBuffer. - if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) { - caps.add(Caps.FloatColorBuffer); - } - } - - if (gl.isExtensionAvailable("GL_ARB_depth_buffer_float")) { - caps.add(Caps.FloatDepthBuffer); - } - - if (caps.contains(Caps.OpenGL30)) { - caps.add(Caps.PackedDepthStencilBuffer); - } - - if (gl.isExtensionAvailable("GL_ARB_draw_instanced") || gl.isExtensionAvailable("GL_ARB_instanced_arrays")) { - caps.add(Caps.MeshInstancing); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_buffer_object")) { - caps.add(Caps.TextureBuffer); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_float")) { - if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) { - caps.add(Caps.FloatTexture); - } - } - - if (gl.isExtensionAvailable("GL_ARB_vertex_array_object")) { - caps.add(Caps.VertexBufferArray); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two")) { - caps.add(Caps.NonPowerOfTwoTextures); - } - else { - logger.log(Level.WARNING, "Your graphics card does not " - + "support non-power-of-2 textures. " - + "Some features might not work."); - } - - if (gl.isExtensionAvailable("GL_EXT_packed_float") || gl.isExtensionAvailable("GL_VERSION_3_0")) { - // This format is part of the OGL3 specification - caps.add(Caps.PackedFloatColorBuffer); - if (gl.isExtensionAvailable("GL_ARB_half_float_pixel")) { - // because textures are usually uploaded as RGB16F - // need half-float pixel - caps.add(Caps.PackedFloatTexture); - } - } - - if (gl.isExtensionAvailable("GL_EXT_texture_array") || gl.isExtensionAvailable("GL_VERSION_3_0")) { - caps.add(Caps.TextureArray); - } - - if (gl.isExtensionAvailable("GL_EXT_texture_shared_exponent") || gl.isExtensionAvailable("GL_VERSION_3_0")) { - caps.add(Caps.SharedExponentTexture); - } - - if (gl.isExtensionAvailable("GL_EXT_framebuffer_object")) { - caps.add(Caps.FrameBuffer); - - gl.glGetIntegerv(GL.GL_MAX_RENDERBUFFER_SIZE, intBuf16); - maxRBSize = intBuf16.get(0); - logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize); - - gl.glGetIntegerv(GL2GL3.GL_MAX_COLOR_ATTACHMENTS, intBuf16); - maxFBOAttachs = intBuf16.get(0); - logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs); - - if (gl.isExtensionAvailable("GL_EXT_framebuffer_multisample")) { - caps.add(Caps.FrameBufferMultisample); - - gl.glGetIntegerv(GL2GL3.GL_MAX_SAMPLES, intBuf16); - maxFBOSamples = intBuf16.get(0); - logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples); - } - - if (gl.isExtensionAvailable("GL_ARB_texture_multisample")) { - caps.add(Caps.TextureMultisample); - - gl.glGetIntegerv(GL3.GL_MAX_COLOR_TEXTURE_SAMPLES, intBuf16); - maxColorTexSamples = intBuf16.get(0); - logger.log(Level.FINER, "Texture Multisample Color Samples: {0}", maxColorTexSamples); - - gl.glGetIntegerv(GL3.GL_MAX_DEPTH_TEXTURE_SAMPLES, intBuf16); - maxDepthTexSamples = intBuf16.get(0); - logger.log(Level.FINER, "Texture Multisample Depth Samples: {0}", maxDepthTexSamples); - } - - gl.glGetIntegerv(GL2ES2.GL_MAX_DRAW_BUFFERS, intBuf16); - maxMRTFBOAttachs = intBuf16.get(0); - if (maxMRTFBOAttachs > 1) { - caps.add(Caps.FrameBufferMRT); - logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs); - } - - //if (gl.isExtensionAvailable("GL_ARB_draw_buffers")) { - // caps.add(Caps.FrameBufferMRT); - // gl.glGetIntegerv(GL2GL3.GL_MAX_DRAW_BUFFERS, intBuf16); - // maxMRTFBOAttachs = intBuf16.get(0); - // logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs); - //} - } - - if (gl.isExtensionAvailable("GL_ARB_multisample")) { - gl.glGetIntegerv(GL.GL_SAMPLE_BUFFERS, intBuf16); - boolean available = intBuf16.get(0) != 0; - gl.glGetIntegerv(GL.GL_SAMPLES, intBuf16); - int samples = intBuf16.get(0); - logger.log(Level.FINER, "Samples: {0}", samples); - boolean enabled = gl.glIsEnabled(GL.GL_MULTISAMPLE); - if (samples > 0 && available && !enabled) { - gl.glEnable(GL.GL_MULTISAMPLE); - } - caps.add(Caps.Multisample); - } - - //supports sRGB pipeline - if ((gl.isExtensionAvailable("GL_ARB_framebuffer_sRGB") && gl.isExtensionAvailable("GL_EXT_texture_sRGB")) || gl.isExtensionAvailable("GL_VERSION_3_0")){ - caps.add(Caps.Srgb); - } - - logger.log(Level.FINE, "Caps: {0}", caps); - } - - @Override - public void invalidateState() { - context.reset(); - boundShader = null; - lastFb = null; - - GL gl = GLContext.getCurrentGL(); - gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, intBuf1); - initialDrawBuf = intBuf1.get(0); - gl.glGetIntegerv(GL2GL3.GL_READ_BUFFER, intBuf1); - initialReadBuf = intBuf1.get(0); - } - - @Override - public void resetGLObjects() { - logger.log(Level.FINE, "Reseting objects and invalidating state"); - objManager.resetObjects(); - statistics.clearMemory(); - invalidateState(); - } - - @Override - public void cleanup() { - logger.log(Level.FINE, "Deleting objects and invalidating state"); - objManager.deleteAllObjects(this); - statistics.clearMemory(); - invalidateState(); - } - -// private void checkCap(Caps cap) { -// if (!caps.contains(cap)) { -// throw new UnsupportedOperationException("Required capability missing: " + cap.name()); -// } -// } - - /*********************************************************************\ - |* Render State *| - \*********************************************************************/ - @Override - public void setDepthRange(float start, float end) { - GL gl = GLContext.getCurrentGL(); - gl.glDepthRange(start, end); - } - - @Override - public void clearBuffers(boolean color, boolean depth, boolean stencil) { - GL gl = GLContext.getCurrentGL(); - 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) { - gl.glColorMask(true, true, true, true); - context.colorWriteEnabled = true; - } - bits = GL.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) { - gl.glDepthMask(true); - context.depthWriteEnabled = true; - } - bits |= GL.GL_DEPTH_BUFFER_BIT; - } - if (stencil) { - bits |= GL.GL_STENCIL_BUFFER_BIT; - } - if (bits != 0) { - gl.glClear(bits); - } - } - - @Override - public void setBackgroundColor(ColorRGBA color) { - GL gl = GLContext.getCurrentGL(); - gl.glClearColor(color.r, color.g, color.b, color.a); - } - - @Override - public void setAlphaToCoverage(boolean value) { - if (caps.contains(Caps.Multisample)) { - GL gl = GLContext.getCurrentGL(); - if (value) { - gl.glEnable(GL.GL_SAMPLE_ALPHA_TO_COVERAGE); - } else { - gl.glDisable(GL.GL_SAMPLE_ALPHA_TO_COVERAGE); - } - } - } - - @Override - public void applyRenderState(RenderState state) { - GL gl = GLContext.getCurrentGL(); - if (state.isWireframe() && !context.wireframe) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); - } - context.wireframe = true; - } else if (!state.isWireframe() && context.wireframe) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL); - } - context.wireframe = false; - } - - if (state.isDepthTest() && !context.depthTestEnabled) { - gl.glEnable(GL.GL_DEPTH_TEST); - gl.glDepthFunc(convertTestFunction(context.depthFunc)); - context.depthTestEnabled = true; - } else if (!state.isDepthTest() && context.depthTestEnabled) { - gl.glDisable(GL.GL_DEPTH_TEST); - context.depthTestEnabled = false; - } - if (state.getDepthFunc() != context.depthFunc) { - gl.glDepthFunc(convertTestFunction(state.getDepthFunc())); - context.depthFunc = state.getDepthFunc(); - } - if (gl.isGL2ES1()) { - if (state.isAlphaTest() && !context.alphaTestEnabled) { - gl.glEnable(GL2ES1.GL_ALPHA_TEST); - gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff); - context.alphaTestEnabled = true; - } else if (!state.isAlphaTest() && context.alphaTestEnabled) { - gl.glDisable(GL2ES1.GL_ALPHA_TEST); - context.alphaTestEnabled = false; - } - if (state.getAlphaFallOff() != context.alphaTestFallOff) { - gl.getGL2ES1().glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff); - context.alphaTestFallOff = state.getAlphaFallOff(); - } - if (state.getAlphaFunc() != context.alphaFunc) { - gl.getGL2ES1().glAlphaFunc(convertTestFunction(state.getAlphaFunc()), context.alphaTestFallOff); - context.alphaFunc = state.getAlphaFunc(); - } - } - - if (state.isDepthWrite() && !context.depthWriteEnabled) { - gl.glDepthMask(true); - context.depthWriteEnabled = true; - } else if (!state.isDepthWrite() && context.depthWriteEnabled) { - gl.glDepthMask(false); - context.depthWriteEnabled = false; - } - - if (state.isColorWrite() && !context.colorWriteEnabled) { - gl.glColorMask(true, true, true, true); - context.colorWriteEnabled = true; - } else if (!state.isColorWrite() && context.colorWriteEnabled) { - gl.glColorMask(false, false, false, false); - context.colorWriteEnabled = false; - } - - 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; - } - if (gl.isGL2ES1()) { - gl.glEnable(GL2ES1.GL_POINT_SPRITE); - } - if (gl.isGL2GL3()) { - gl.glEnable(GL2GL3.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; - } - if (gl.isGL2ES1()) { - gl.glDisable(GL2ES1.GL_POINT_SPRITE); - } - if (gl.isGL2GL3()) { - gl.glDisable(GL2GL3.GL_VERTEX_PROGRAM_POINT_SIZE); - } - context.pointSprite = false; - } - } - - if (state.isPolyOffset()) { - if (!context.polyOffsetEnabled) { - gl.glEnable(GL.GL_POLYGON_OFFSET_FILL); - gl.glPolygonOffset(state.getPolyOffsetFactor(), - state.getPolyOffsetUnits()); - context.polyOffsetEnabled = true; - context.polyOffsetFactor = state.getPolyOffsetFactor(); - context.polyOffsetUnits = state.getPolyOffsetUnits(); - } else { - if (state.getPolyOffsetFactor() != context.polyOffsetFactor - || state.getPolyOffsetUnits() != context.polyOffsetUnits) { - gl.glPolygonOffset(state.getPolyOffsetFactor(), - state.getPolyOffsetUnits()); - context.polyOffsetFactor = state.getPolyOffsetFactor(); - context.polyOffsetUnits = state.getPolyOffsetUnits(); - } - } - } else { - if (context.polyOffsetEnabled) { - gl.glDisable(GL.GL_POLYGON_OFFSET_FILL); - context.polyOffsetEnabled = false; - context.polyOffsetFactor = 0; - context.polyOffsetUnits = 0; - } - } - - if (state.getFaceCullMode() != context.cullMode) { - if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) { - gl.glDisable(GL.GL_CULL_FACE); - } else { - gl.glEnable(GL.GL_CULL_FACE); - } - - switch (state.getFaceCullMode()) { - case Off: - break; - case Back: - gl.glCullFace(GL.GL_BACK); - break; - case Front: - gl.glCullFace(GL.GL_FRONT); - break; - case FrontAndBack: - gl.glCullFace(GL.GL_FRONT_AND_BACK); - 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) { - gl.glDisable(GL.GL_BLEND); - } else { - gl.glEnable(GL.GL_BLEND); - switch (state.getBlendMode()) { - case Off: - break; - case Additive: - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE); - break; - 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; - case PremultAlpha: - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); - break; - case Modulate: - gl.glBlendFunc(GL.GL_DST_COLOR, GL.GL_ZERO); - break; - case ModulateX2: - gl.glBlendFunc(GL.GL_DST_COLOR, GL.GL_SRC_COLOR); - break; - case Screen: - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_COLOR); - break; - case Exclusion: - gl.glBlendFunc(GL.GL_ONE_MINUS_DST_COLOR, GL.GL_ONE_MINUS_SRC_COLOR); - break; - default: - throw new UnsupportedOperationException("Unrecognized blend mode: " - + state.getBlendMode()); - } - } - - context.blendMode = state.getBlendMode(); - } - - if (context.stencilTest != state.isStencilTest() - || context.frontStencilStencilFailOperation != state.getFrontStencilStencilFailOperation() - || context.frontStencilDepthFailOperation != state.getFrontStencilDepthFailOperation() - || context.frontStencilDepthPassOperation != state.getFrontStencilDepthPassOperation() - || context.backStencilStencilFailOperation != state.getBackStencilStencilFailOperation() - || context.backStencilDepthFailOperation != state.getBackStencilDepthFailOperation() - || context.backStencilDepthPassOperation != state.getBackStencilDepthPassOperation() - || context.frontStencilFunction != state.getFrontStencilFunction() - || context.backStencilFunction != state.getBackStencilFunction()) { - - context.frontStencilStencilFailOperation = state.getFrontStencilStencilFailOperation(); //terrible looking, I know - context.frontStencilDepthFailOperation = state.getFrontStencilDepthFailOperation(); - context.frontStencilDepthPassOperation = state.getFrontStencilDepthPassOperation(); - context.backStencilStencilFailOperation = state.getBackStencilStencilFailOperation(); - context.backStencilDepthFailOperation = state.getBackStencilDepthFailOperation(); - context.backStencilDepthPassOperation = state.getBackStencilDepthPassOperation(); - context.frontStencilFunction = state.getFrontStencilFunction(); - context.backStencilFunction = state.getBackStencilFunction(); - - if (state.isStencilTest()) { - gl.glEnable(GL.GL_STENCIL_TEST); - gl.getGL2ES2().glStencilOpSeparate(GL.GL_FRONT, - convertStencilOperation(state.getFrontStencilStencilFailOperation()), - convertStencilOperation(state.getFrontStencilDepthFailOperation()), - convertStencilOperation(state.getFrontStencilDepthPassOperation())); - gl.getGL2ES2().glStencilOpSeparate(GL.GL_BACK, - convertStencilOperation(state.getBackStencilStencilFailOperation()), - convertStencilOperation(state.getBackStencilDepthFailOperation()), - convertStencilOperation(state.getBackStencilDepthPassOperation())); - gl.getGL2ES2().glStencilFuncSeparate(GL.GL_FRONT, - convertTestFunction(state.getFrontStencilFunction()), - 0, Integer.MAX_VALUE); - gl.getGL2ES2().glStencilFuncSeparate(GL.GL_BACK, - convertTestFunction(state.getBackStencilFunction()), - 0, Integer.MAX_VALUE); - } else { - gl.glDisable(GL.GL_STENCIL_TEST); - } - } - } - - private int convertStencilOperation(RenderState.StencilOperation stencilOp) { - switch (stencilOp) { - case Keep: - return GL.GL_KEEP; - case Zero: - return GL.GL_ZERO; - case Replace: - return GL.GL_REPLACE; - case Increment: - return GL.GL_INCR; - case IncrementWrap: - return GL.GL_INCR_WRAP; - case Decrement: - return GL.GL_DECR; - case DecrementWrap: - return GL.GL_DECR_WRAP; - case Invert: - return GL.GL_INVERT; - default: - throw new UnsupportedOperationException("Unrecognized stencil operation: " + stencilOp); - } - } - - private int convertTestFunction(RenderState.TestFunction testFunc) { - switch (testFunc) { - case Never: - return GL.GL_NEVER; - case Less: - return GL.GL_LESS; - case LessOrEqual: - return GL.GL_LEQUAL; - case Greater: - return GL.GL_GREATER; - case GreaterOrEqual: - return GL.GL_GEQUAL; - case Equal: - return GL.GL_EQUAL; - case NotEqual: - return GL.GL_NOTEQUAL; - case Always: - return GL.GL_ALWAYS; - default: - throw new UnsupportedOperationException("Unrecognized test function: " + testFunc); - } - } - - /*********************************************************************\ - |* Camera and World transforms *| - \*********************************************************************/ - @Override - public void setViewPort(int x, int y, int w, int h) { - if (x != vpX || vpY != y || vpW != w || vpH != h) { - GL gl = GLContext.getCurrentGL(); - gl.glViewport(x, y, w, h); - vpX = x; - vpY = y; - vpW = w; - vpH = h; - } - } - - @Override - public void setClipRect(int x, int y, int width, int height) { - GL gl = GLContext.getCurrentGL(); - if (!context.clipRectEnabled) { - gl.glEnable(GL.GL_SCISSOR_TEST); - context.clipRectEnabled = true; - } - if (clipX != x || clipY != y || clipW != width || clipH != height) { - gl.glScissor(x, y, width, height); - clipX = x; - clipY = y; - clipW = width; - clipH = height; - } - } - - @Override - public void clearClipRect() { - if (context.clipRectEnabled) { - GL gl = GLContext.getCurrentGL(); - gl.glDisable(GL.GL_SCISSOR_TEST); - context.clipRectEnabled = false; - - clipX = 0; - clipY = 0; - clipW = 0; - clipH = 0; - } - } - - @Override - public void postFrame() { - objManager.deleteUnused(this); - } - - /*********************************************************************\ - |* Shaders *| - \*********************************************************************/ - protected void updateUniformLocation(Shader shader, Uniform uniform) { - GL gl = GLContext.getCurrentGL(); - // passing a null terminated string is not necessary with JOGL 2.0 - int loc = gl.getGL2ES2().glGetUniformLocation(shader.getId(), uniform.getName()); - if (loc < 0) { - uniform.setLocation(-1); - // uniform is not declared in shader - logger.log(Level.FINE, "Uniform {0} is not declared in shader {1}.", new Object[]{uniform.getName(), shader.getSources()}); - - } else { - uniform.setLocation(loc); - } - } - - protected void bindProgram(Shader shader) { - int shaderId = shader.getId(); - if (context.boundShaderProgram != shaderId) { - GL gl = GLContext.getCurrentGL(); - gl.getGL2ES2().glUseProgram(shaderId); - statistics.onShaderUse(shader, true); - boundShader = shader; - context.boundShaderProgram = shaderId; - } else { - statistics.onShaderUse(shader, false); - } - } - - protected void updateUniform(Shader shader, Uniform uniform) { - int shaderId = shader.getId(); - - assert uniform.getName() != null; - assert shaderId > 0; - - bindProgram(shader); - - 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) { - return; // value not set yet.. - } - statistics.onUniformSet(); - - uniform.clearUpdateNeeded(); - FloatBuffer fb; - IntBuffer ib; - GL gl = GLContext.getCurrentGL(); - switch (uniform.getVarType()) { - case Float: - Float f = (Float) uniform.getValue(); - gl.getGL2ES2().glUniform1f(loc, f.floatValue()); - break; - case Vector2: - Vector2f v2 = (Vector2f) uniform.getValue(); - gl.getGL2ES2().glUniform2f(loc, v2.getX(), v2.getY()); - break; - case Vector3: - Vector3f v3 = (Vector3f) uniform.getValue(); - gl.getGL2ES2().glUniform3f(loc, v3.getX(), v3.getY(), v3.getZ()); - break; - case Vector4: - Object val = uniform.getValue(); - if (val instanceof ColorRGBA) { - ColorRGBA c = (ColorRGBA) val; - gl.getGL2ES2().glUniform4f(loc, c.r, c.g, c.b, c.a); - } else if (val instanceof Vector4f) { - Vector4f c = (Vector4f) val; - gl.getGL2ES2().glUniform4f(loc, c.x, c.y, c.z, c.w); - } else { - Quaternion c = (Quaternion) uniform.getValue(); - gl.getGL2ES2().glUniform4f(loc, c.getX(), c.getY(), c.getZ(), c.getW()); - } - break; - case Boolean: - Boolean b = (Boolean) uniform.getValue(); - gl.getGL2ES2().glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE); - break; - case Matrix3: - fb = (FloatBuffer) uniform.getValue(); - assert fb.remaining() == 9; - gl.getGL2ES2().glUniformMatrix3fv(loc, 1, false, fb); - break; - case Matrix4: - fb = (FloatBuffer) uniform.getValue(); - assert fb.remaining() == 16; - gl.getGL2ES2().glUniformMatrix4fv(loc, 1, false, fb); - break; - case IntArray: - ib = (IntBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform1iv(loc, ib.remaining(), ib); - break; - case FloatArray: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform1fv(loc, fb.remaining(), fb); - break; - case Vector2Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform2fv(loc, fb.remaining(), fb); - break; - case Vector3Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform3fv(loc, fb.remaining(), fb); - break; - case Vector4Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniform4fv(loc, fb.remaining(), fb); - break; - case Matrix4Array: - fb = (FloatBuffer) uniform.getValue(); - gl.getGL2ES2().glUniformMatrix4fv(loc, 1, false, fb); - break; - case Int: - Integer i = (Integer) uniform.getValue(); - gl.getGL2ES2().glUniform1i(loc, i.intValue()); - break; - default: - throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType()); - } - } - - 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 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 int convertShaderType(Shader.ShaderType type) { - switch (type) { - case Fragment: - return GL2ES2.GL_FRAGMENT_SHADER; - case Vertex: - return GL2ES2.GL_VERTEX_SHADER; -// case Geometry: -// return GL3.GL_GEOMETRY_SHADER_ARB; - default: - throw new UnsupportedOperationException("Unrecognized shader type."); - } - } - - public void updateShaderSourceData(ShaderSource source) { - int id = source.getId(); - GL gl = GLContext.getCurrentGL(); - if (id == -1) { - // Create id -// if (gl.isGL2ES2()) { - id = gl.getGL2ES2().glCreateShader(convertShaderType(source.getType())); -// } -// else { -// if (gl.isGL2()) { -// id = gl.getGL2().glCreateShaderObjectARB(convertShaderType(source.getType())); -// } -// } - if (id <= 0) { - throw new RendererException("Invalid ID received when trying to create shader."); - } - - source.setId(id); - } else { - throw new RendererException("Cannot recompile shader source"); - } - - // Upload shader source. - // Merge the defines and source code. - String language = source.getLanguage(); - stringBuf.setLength(0); - if (language.startsWith("GLSL")) { - int version = Integer.parseInt(language.substring(4)); - if (version > 100) { - stringBuf.append("#version "); - stringBuf.append(language.substring(4)); - if (version >= 150) { - stringBuf.append(" core"); - } - stringBuf.append("\n"); - } - } - updateNameBuffer(); - - byte[] definesCodeData = source.getDefines().getBytes(); - byte[] sourceCodeData = source.getSource().getBytes(); - ByteBuffer codeBuf = BufferUtils.createByteBuffer(nameBuf.limit() - + definesCodeData.length - + sourceCodeData.length); - codeBuf.put(nameBuf); - codeBuf.put(definesCodeData); - codeBuf.put(sourceCodeData); - codeBuf.flip(); - - byte[] array = new byte[codeBuf.limit()]; - codeBuf.rewind(); - codeBuf.get(array); - codeBuf.rewind(); - - gl.getGL2ES2().glShaderSource(id, 1, new String[]{new String(array)}, new int[]{array.length}, 0); - gl.getGL2ES2().glCompileShader(id); - - gl.getGL2ES2().glGetShaderiv(id, GL2ES2.GL_COMPILE_STATUS, intBuf1); - - boolean compiledOK = intBuf1.get(0) == GL.GL_TRUE; - String infoLog = null; - - if (VALIDATE_SHADER || !compiledOK) { - // even if compile succeeded, check - // log for warnings - gl.getGL2ES2().glGetShaderiv(id, GL2ES2.GL_INFO_LOG_LENGTH, intBuf1); - int length = intBuf1.get(0); - if (length > 3) { - // get infos - ByteBuffer logBuf = BufferUtils.createByteBuffer(length); - gl.getGL2ES2().glGetShaderInfoLog(id, length, null, logBuf); - byte[] logBytes = new byte[length]; - logBuf.get(logBytes, 0, length); - // convert to string, etc - infoLog = new String(logBytes); - } - } - - if (compiledOK) { - if (infoLog != null) { - logger.log(Level.FINE, "{0} compile success\n{1}", - new Object[]{source.getName(), infoLog}); - } else { - logger.log(Level.FINE, "{0} compile success", 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 void updateShaderData(Shader shader) { - GL gl = GLContext.getCurrentGL(); - int id = shader.getId(); - boolean needRegister = false; - if (id == -1) { - // create program - id = gl.getGL2ES2().glCreateProgram(); - if (id == 0) { - throw new RendererException("Invalid ID (" + id + ") received when trying to create shader program."); - } - - shader.setId(id); - needRegister = true; - } - - for (ShaderSource source : shader.getSources()) { - if (source.isUpdateNeeded()) { - updateShaderSourceData(source); - } - gl.getGL2ES2().glAttachShader(id, source.getId()); - } - - if (gl.isGL2GL3() && gl.isExtensionAvailable("GL_EXT_gpu_shader4")) { - // Check if GLSL version is 1.5 for shader - gl.getGL2GL3().glBindFragDataLocation(id, 0, "outFragColor"); - // For MRT - for (int i = 0; i < maxMRTFBOAttachs; i++) { - gl.getGL2GL3().glBindFragDataLocation(id, i, "outFragData[" + i + "]"); - } - } - - // Link shaders to program - gl.getGL2ES2().glLinkProgram(id); - - // Check link status - gl.getGL2ES2().glGetProgramiv(id, GL2ES2.GL_LINK_STATUS, intBuf1); - boolean linkOK = intBuf1.get(0) == GL.GL_TRUE; - String infoLog = null; - - if (VALIDATE_SHADER || !linkOK) { - gl.getGL2ES2().glGetProgramiv(id, GL2ES2.GL_INFO_LOG_LENGTH, intBuf1); - int length = intBuf1.get(0); - if (length > 3) { - // get infos - ByteBuffer logBuf = BufferUtils.createByteBuffer(length); - gl.getGL2ES2().glGetProgramInfoLog(id, length, null, logBuf); - - // convert to string, etc - byte[] logBytes = new byte[length]; - logBuf.get(logBytes, 0, length); - infoLog = new String(logBytes); - } - } - - 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 failed to link, shader:" + shader + "\n" + infoLog); - } else { - throw new RendererException("Shader failed to link, shader:" + shader + "\ninfo: "); - } - } - } - - @Override - public void setShader(Shader shader) { - 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); - } - } - - @Override - public void deleteShaderSource(ShaderSource source) { - if (source.getId() < 0) { - logger.warning("Shader source is not uploaded to GPU, cannot delete."); - return; - } - source.clearUpdateNeeded(); - GL gl = GLContext.getCurrentGL(); - gl.getGL2ES2().glDeleteShader(source.getId()); - source.resetObject(); - } - - @Override - public void deleteShader(Shader shader) { - if (shader.getId() == -1) { - logger.warning("Shader is not uploaded to GPU, cannot delete."); - return; - } - - GL gl = GLContext.getCurrentGL(); - for (ShaderSource source : shader.getSources()) { - if (source.getId() != -1) { - gl.getGL2ES2().glDetachShader(shader.getId(), source.getId()); - deleteShaderSource(source); - } - } - - gl.getGL2ES2().glDeleteProgram(shader.getId()); - statistics.onDeleteShader(); - shader.resetObject(); - } - - /*********************************************************************\ - |* Framebuffers *| - \*********************************************************************/ - @Override - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { - GL gl = GLContext.getCurrentGL(); - if (gl.isExtensionAvailable("GL_EXT_framebuffer_blit") && gl.isGL2GL3()) { - int srcX0 = 0; - int srcY0 = 0; - int srcX1; - int srcY1; - - int dstX0 = 0; - int dstY0 = 0; - int dstX1; - int dstY1; - - int prevFBO = context.boundFBO; - - if (mainFbOverride != null) { - if (src == null) { - src = mainFbOverride; - } - if (dst == null) { - dst = mainFbOverride; - } - } - - if (src != null && src.isUpdateNeeded()) { - updateFrameBuffer(src); - } - - if (dst != null && dst.isUpdateNeeded()) { - updateFrameBuffer(dst); - } - - if (src == null) { - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, 0); - srcX0 = vpX; - srcY0 = vpY; - srcX1 = vpX + vpW; - srcY1 = vpY + vpH; - } else { - gl.glBindFramebuffer(GL2GL3.GL_READ_FRAMEBUFFER, src.getId()); - srcX1 = src.getWidth(); - srcY1 = src.getHeight(); - } - if (dst == null) { - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, 0); - dstX0 = vpX; - dstY0 = vpY; - dstX1 = vpX + vpW; - dstY1 = vpY + vpH; - } else { - gl.glBindFramebuffer(GL2GL3.GL_DRAW_FRAMEBUFFER, dst.getId()); - dstX1 = dst.getWidth(); - dstY1 = dst.getHeight(); - } - int mask = GL.GL_COLOR_BUFFER_BIT; - if (copyDepth) { - mask |= GL.GL_DEPTH_BUFFER_BIT; - } - gl.getGL2GL3().glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, - dstX0, dstY0, dstX1, dstY1, mask, - GL.GL_NEAREST); - gl.glBindFramebuffer(GL2GL3.GL_FRAMEBUFFER, prevFBO); - - - try { - checkFrameBufferError(); - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "Source FBO:\n{0}", src); - logger.log(Level.SEVERE, "Dest FBO:\n{0}", dst); - throw ex; - } - } else { - throw new RendererException("EXT_framebuffer_blit required."); - // TODO: support non-blit copies? - } - } - - private String getTargetBufferName(int buffer) { - switch (buffer) { - case GL.GL_NONE: - return "NONE"; - case GL.GL_FRONT: - return "GL_FRONT"; - case GL.GL_BACK: - return "GL_BACK"; - default: - if (buffer >= GL.GL_COLOR_ATTACHMENT0 - && buffer <= GL2ES2.GL_COLOR_ATTACHMENT15) { - return "GL_COLOR_ATTACHMENT" - + (buffer - GL.GL_COLOR_ATTACHMENT0); - } else { - return "UNKNOWN? " + buffer; - } - } - } - - private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name) { - GL gl = GLContext.getCurrentGL(); - System.out.println("== Renderbuffer " + name + " =="); - System.out.println("RB ID: " + rb.getId()); - System.out.println("Is proper? " + gl.glIsRenderbuffer(rb.getId())); - - int attachment = convertAttachmentSlot(rb.getSlot()); - - gl.glGetFramebufferAttachmentParameteriv(GL2GL3.GL_DRAW_FRAMEBUFFER, - attachment, - GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, intBuf16); - int type = intBuf16.get(0); - gl.glGetFramebufferAttachmentParameteriv(GL2GL3.GL_DRAW_FRAMEBUFFER, - attachment, - GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, intBuf16); - int rbName = intBuf16.get(0); - - switch (type) { - case GL.GL_NONE: - System.out.println("Type: None"); - break; - case GL.GL_TEXTURE: - System.out.println("Type: Texture"); - break; - case GL.GL_RENDERBUFFER: - System.out.println("Type: Buffer"); - System.out.println("RB ID: " + rbName); - break; - } - - - - } - - private void printRealFrameBufferInfo(FrameBuffer fb) { - GL gl = GLContext.getCurrentGL(); - final byte[] param = new byte[1]; - gl.glGetBooleanv(GL2GL3.GL_DOUBLEBUFFER, param, 0); - boolean doubleBuffer = param[0] != (byte) 0x00; - gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, intBuf16); - String drawBuf = getTargetBufferName(intBuf16.get(0)); - gl.glGetIntegerv(GL2GL3.GL_READ_BUFFER, intBuf16); - String readBuf = getTargetBufferName(intBuf16.get(0)); - - int fbId = fb.getId(); - gl.glGetIntegerv(GL2GL3.GL_DRAW_FRAMEBUFFER_BINDING, intBuf16); - int curDrawBinding = intBuf16.get(0); - gl.glGetIntegerv(GL2GL3.GL_READ_FRAMEBUFFER_BINDING, intBuf16); - int curReadBinding = intBuf16.get(0); - - System.out.println("=== OpenGL FBO State ==="); - System.out.println("Context doublebuffered? " + doubleBuffer); - System.out.println("FBO ID: " + fbId); - System.out.println("Is proper? " + gl.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) { - gl.glBindFramebuffer(GL2GL3.GL_DRAW_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); - } - } - - private void checkFrameBufferError() { - GL gl = GLContext.getCurrentGL(); - int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER); - switch (status) { - case GL.GL_FRAMEBUFFER_COMPLETE: - break; - case GL.GL_FRAMEBUFFER_UNSUPPORTED: - // Choose different formats - throw new IllegalStateException("Framebuffer object format is " - + "unsupported by the video hardware."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - throw new IllegalStateException("Framebuffer has erronous attachment."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - throw new IllegalStateException("Framebuffer is missing required attachment."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - throw new IllegalStateException( - "Framebuffer attachments must have same dimensions."); - case GL.GL_FRAMEBUFFER_INCOMPLETE_FORMATS: - throw new IllegalStateException("Framebuffer attachments must have same formats."); - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - throw new IllegalStateException("Incomplete draw buffer."); - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - throw new IllegalStateException("Incomplete read buffer."); - case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - 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. "); - } - } - - private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { - GL gl = GLContext.getCurrentGL(); - int id = rb.getId(); - if (id == -1) { - gl.glGenRenderbuffers(1, intBuf1); - id = intBuf1.get(0); - rb.setId(id); - } - - if (context.boundRB != id) { - gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, id); - context.boundRB = id; - } - - if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) { - throw new RendererException("Resolution " + fb.getWidth() - + ":" + fb.getHeight() + " is not supported."); - } - - TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(rb.getFormat(), fb.isSrgb()); - - if (fb.getSamples() > 1 && gl.isExtensionAvailable("GL_EXT_framebuffer_multisample") - && gl.isGL2GL3()/*&& gl.isFunctionAvailable("glRenderbufferStorageMultisample")*/) { - int samples = fb.getSamples(); - if (maxFBOSamples < samples) { - samples = maxFBOSamples; - } - gl.getGL2GL3() - .glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, samples, - glFmt.internalFormat, fb.getWidth(), - fb.getHeight()); - } else { - gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, - glFmt.internalFormat, fb.getWidth(), fb.getHeight()); - } - } - - private int convertAttachmentSlot(int attachmentSlot) { - // can also add support for stencil here - if (attachmentSlot == FrameBuffer.SLOT_DEPTH) { - return GL.GL_DEPTH_ATTACHMENT; - } else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) { - return GL2ES3.GL_DEPTH_STENCIL_ATTACHMENT; - } else if (attachmentSlot < 0 || attachmentSlot >= 16) { - throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot); - } - - return GL.GL_COLOR_ATTACHMENT0 + attachmentSlot; - } - - public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) { - GL gl = GLContext.getCurrentGL(); - Texture tex = rb.getTexture(); - Image image = tex.getImage(); - if (image.isUpdateNeeded()) { - updateTexImageData(image, tex.getType(), 0); - - // NOTE: For depth textures, sets nearest/no-mips mode - // Required to fix "framebuffer unsupported" - // for old NVIDIA drivers! - setupTextureParams(tex); - } - - gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, - convertAttachmentSlot(rb.getSlot()), - convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()), - image.getId(), - 0); - } - - 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) { - GL gl = GLContext.getCurrentGL(); - gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, convertAttachmentSlot(rb.getSlot()), - GL.GL_RENDERBUFFER, rb.getId()); - } - } - - public void updateFrameBuffer(FrameBuffer fb) { - GL gl = GLContext.getCurrentGL(); - int id = fb.getId(); - if (id == -1) { - // create FBO - gl.glGenFramebuffers(1, intBuf1); - id = intBuf1.get(0); - fb.setId(id); - objManager.registerObject(fb); - - statistics.onNewFrameBuffer(); - } - - if (context.boundFBO != id) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, id); - // 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 Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) { - if (fb.getSamples() <= 1) { - throw new IllegalArgumentException("Framebuffer must be multisampled"); - } - - setFrameBuffer(fb); - - Vector2f[] samplePositions = new Vector2f[fb.getSamples()]; - FloatBuffer samplePos = BufferUtils.createFloatBuffer(2); - GL gl = GLContext.getCurrentGL(); - if (gl.isGL2GL3()) { - for (int i = 0; i < samplePositions.length; i++) { - gl.getGL3().glGetMultisamplefv(GL3.GL_SAMPLE_POSITION, i, samplePos); - samplePos.clear(); - samplePositions[i] = new Vector2f(samplePos.get(0) - 0.5f, - samplePos.get(1) - 0.5f); - } - } - return samplePositions; - } - - @Override - public void setMainFrameBufferOverride(FrameBuffer fb) { - mainFbOverride = fb; - } - - @Override - public void setFrameBuffer(FrameBuffer fb) { - GL gl = GLContext.getCurrentGL(); - if (!gl.isExtensionAvailable("GL_EXT_framebuffer_object")) { - throw new RendererException("Framebuffer objects are not supported" + - " by the video hardware"); - } - - 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()); - gl.glEnable(textureType); - gl.glGenerateMipmap(textureType); - gl.glDisable(textureType); - } - } - } - - if (fb == null) { - // unbind any fbos - if (context.boundFBO != 0) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); - statistics.onFrameBufferUse(null, true); - - context.boundFBO = 0; - } - // select back buffer - if (context.boundDrawBuf != -1) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffer(initialDrawBuf); - } - context.boundDrawBuf = -1; - } - if (context.boundReadBuf != -1) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().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()) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fb.getId()); - 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) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffer(GL.GL_NONE); - } - context.boundDrawBuf = -2; - } - if (context.boundReadBuf != -2) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glReadBuffer(GL.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 < fb.getNumColorBuffers(); i++) { - intBuf16.put(GL.GL_COLOR_ATTACHMENT0 + i); - } - - intBuf16.flip(); - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffers(intBuf16.limit(), intBuf16); - } - context.boundDrawBuf = 100 + fb.getNumColorBuffers(); - } - } else { - RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); - // select this draw buffer - if (context.boundDrawBuf != rb.getSlot()) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawBuffer(GL.GL_COLOR_ATTACHMENT0 + rb.getSlot()); - } - context.boundDrawBuf = rb.getSlot(); - } - } - } - - assert fb.getId() >= 0; - assert context.boundFBO == fb.getId(); - lastFb = fb; - - try { - checkFrameBufferError(); - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb); - printRealFrameBufferInfo(fb); - throw ex; - } - } - } - - @Override - public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { - GL gl = GLContext.getCurrentGL(); - if (fb != null) { - RenderBuffer rb = fb.getColorBuffer(); - if (rb == null) { - throw new IllegalArgumentException("Specified framebuffer" - + " does not have a colorbuffer"); - } - - setFrameBuffer(fb); - if (context.boundReadBuf != rb.getSlot()) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glReadBuffer(GL.GL_COLOR_ATTACHMENT0 + rb.getSlot()); - } - context.boundReadBuf = rb.getSlot(); - } - } else { - setFrameBuffer(null); - } - - gl.glReadPixels(vpX, vpY, vpW, vpH, /*GL.GL_RGBA*/ GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, byteBuf); - } - - private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) { - intBuf1.put(0, rb.getId()); - GL gl = GLContext.getCurrentGL(); - gl.glDeleteRenderbuffers(1, intBuf1); - } - - @Override - public void deleteFrameBuffer(FrameBuffer fb) { - if (fb.getId() != -1) { - GL gl = GLContext.getCurrentGL(); - if (context.boundFBO == fb.getId()) { - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); - context.boundFBO = 0; - } - - if (fb.getDepthBuffer() != null) { - deleteRenderBuffer(fb, fb.getDepthBuffer()); - } - if (fb.getColorBuffer() != null) { - deleteRenderBuffer(fb, fb.getColorBuffer()); - } - - intBuf1.put(0, fb.getId()); - gl.glDeleteFramebuffers(1, intBuf1); - fb.resetObject(); - - statistics.onDeleteFrameBuffer(); - } - } - - /*********************************************************************\ - |* Textures *| - \*********************************************************************/ - private int convertTextureType(Texture.Type type, int samples, int face) { - GL gl = GLContext.getCurrentGL(); - if (samples > 1 && !gl.isExtensionAvailable("GL_ARB_texture_multisample")) { - throw new RendererException("Multisample textures are not supported" + - " by the video hardware."); - } - - switch (type) { - case TwoDimensional: - if (samples > 1) { - return GL3.GL_TEXTURE_2D_MULTISAMPLE; - } else { - return GL.GL_TEXTURE_2D; - } - case TwoDimensionalArray: - if (samples > 1) { - return GL3.GL_TEXTURE_2D_MULTISAMPLE_ARRAY; - } else { - return GL2ES3.GL_TEXTURE_2D_ARRAY; - } - case ThreeDimensional: - return GL2ES2.GL_TEXTURE_3D; - case CubeMap: - if (face < 0) { - return GL.GL_TEXTURE_CUBE_MAP; - } else if (face < 6) { - return GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face; - } else { - throw new UnsupportedOperationException("Invalid cube map face index: " + face); - } - default: - throw new UnsupportedOperationException("Unknown texture type: " + type); - } - } - - private int convertMagFilter(Texture.MagFilter filter) { - switch (filter) { - case Bilinear: - return GL.GL_LINEAR; - case Nearest: - return GL.GL_NEAREST; - default: - throw new UnsupportedOperationException("Unknown mag filter: " + filter); - } - } - - private int convertMinFilter(Texture.MinFilter filter) { - switch (filter) { - case Trilinear: - return GL.GL_LINEAR_MIPMAP_LINEAR; - case BilinearNearestMipMap: - return GL.GL_LINEAR_MIPMAP_NEAREST; - case NearestLinearMipMap: - return GL.GL_NEAREST_MIPMAP_LINEAR; - case NearestNearestMipMap: - return GL.GL_NEAREST_MIPMAP_NEAREST; - case BilinearNoMipMaps: - return GL.GL_LINEAR; - case NearestNoMipMaps: - return GL.GL_NEAREST; - default: - throw new UnsupportedOperationException("Unknown min filter: " + filter); - } - } - - private int convertWrapMode(Texture.WrapMode mode) { - switch (mode) { - case BorderClamp: - return GL2GL3.GL_CLAMP_TO_BORDER; - case Clamp: - // Falldown intentional. - case EdgeClamp: - return GL.GL_CLAMP_TO_EDGE; - case Repeat: - return GL.GL_REPEAT; - case MirroredRepeat: - return GL.GL_MIRRORED_REPEAT; - default: - throw new UnsupportedOperationException("Unknown wrap mode: " + mode); - } - } - - @SuppressWarnings("fallthrough") - private void setupTextureParams(Texture tex) { - GL gl = GLContext.getCurrentGL(); - Image image = tex.getImage(); - int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1); - - // filter things - int minFilter = convertMinFilter(tex.getMinFilter()); - int magFilter = convertMagFilter(tex.getMagFilter()); - gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter); - gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter); - - if (tex.getAnisotropicFilter() > 1) { - if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) { - gl.glTexParameterf(target, - GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, - tex.getAnisotropicFilter()); - } - } - - if (context.pointSprite) { - return; // Attempt to fix glTexParameter crash for some ATI GPUs - } - // repeat modes - switch (tex.getType()) { - case ThreeDimensional: - case CubeMap: // cubemaps use 3D coords - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); - case TwoDimensional: - case TwoDimensionalArray: - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); - // fall down here is intentional.. -// case OneDimensional: - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); - break; - default: - throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); - } - - if (tex.isNeedCompareModeUpdate()) { - // R to Texture compare mode - if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); - gl.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); - if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); - } else { - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); - } - } else { - //restoring default value - gl.glTexParameteri(target, GL2ES2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); - } - tex.compareModeUpdated(); - } - } - - /** - * Uploads the given image to the GL driver. - * - * @param img The image to upload - * @param type How the data in the image argument should be interpreted. - * @param unit The texture slot to be used to upload the image, not important - */ - public void updateTexImageData(Image img, Texture.Type type, int unit) { - int texId = img.getId(); - GL gl = GLContext.getCurrentGL(); - if (texId == -1) { - // create texture - gl.glGenTextures(1, intBuf1); - texId = intBuf1.get(0); - img.setId(texId); - objManager.registerObject(img); - - statistics.onNewTexture(); - } - - // bind texture - int target = convertTextureType(type, img.getMultiSamples(), -1); - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - if (context.boundTextures[unit] != img) { - gl.glBindTexture(target, texId); - context.boundTextures[unit] = img; - - statistics.onTextureUse(img, true); - } - - if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { - // Image does not have mipmaps, but they are required. - // Generate from base level. - - if (!gl.isExtensionAvailable("GL_VERSION_3_0")) { - if (gl.isGL2ES1()) { - gl.glTexParameteri(target, GL2ES1.GL_GENERATE_MIPMAP, GL.GL_TRUE); - img.setMipmapsGenerated(true); - } - } else { - // For OpenGL3 and up. - // We'll generate mipmaps via glGenerateMipmapEXT (see below) - } - } else if (img.hasMipmaps()) { - // Image already has mipmaps, set the max level based on the - // number of mipmaps we have. - if (gl.isGL2GL3()) { - gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_MAX_LEVEL, img.getMipMapSizes().length - 1); - } - } else { - // Image does not have mipmaps and they are not required. - // Specify that that the texture has no mipmaps. - gl.glTexParameteri(target, GL2ES3.GL_TEXTURE_MAX_LEVEL, 0); - } - - int imageSamples = img.getMultiSamples(); - if (imageSamples > 1) { - if (img.getFormat().isDepthFormat()) { - img.setMultiSamples(Math.min(maxDepthTexSamples, imageSamples)); - } else { - img.setMultiSamples(Math.min(maxColorTexSamples, imageSamples)); - } - } - - // Yes, some OpenGL2 cards (GeForce 5) still dont support NPOT. - if (!gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two") && img.isNPOT()) { - if (img.getData(0) == null) { - throw new RendererException("non-power-of-2 framebuffer textures are not supported by the video hardware"); - } else { - MipMapGenerator.resizeToPowerOf2(img); - } - } - - // Check if graphics card doesn't support multisample textures - if (!gl.isExtensionAvailable("GL_ARB_texture_multisample")) { - if (img.getMultiSamples() > 1) { - throw new RendererException("Multisample textures not supported by graphics hardware"); - } - } - - if (target == GL.GL_TEXTURE_CUBE_MAP) { - List data = img.getData(); - if (data.size() != 6) { - logger.log(Level.WARNING, "Invalid texture: {0}\n" - + "Cubemap textures must contain 6 data units.", img); - return; - } - for (int i = 0; i < 6; i++) { - TextureUtil.uploadTexture(img, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, linearizeSrgbImages); - } - } else if (target == GL2ES3.GL_TEXTURE_2D_ARRAY) { - if (!caps.contains(Caps.TextureArray)) { - throw new RendererException("Texture arrays not supported by graphics hardware"); - } - - List data = img.getData(); - - // -1 index specifies prepare data for 2D Array - TextureUtil.uploadTexture(img, target, -1, 0, linearizeSrgbImages); - - for (int i = 0; i < data.size(); i++) { - // upload each slice of 2D array in turn - // this time with the appropriate index - TextureUtil.uploadTexture(img, target, i, 0, linearizeSrgbImages); - } - } else { - TextureUtil.uploadTexture(img, target, 0, 0, linearizeSrgbImages); - } - - if (img.getMultiSamples() != imageSamples) { - img.setMultiSamples(imageSamples); - } - - if (gl.isExtensionAvailable("GL_VERSION_3_0")) { - if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired() && img.getData() != null) { - // XXX: Required for ATI - gl.glEnable(target); - gl.glGenerateMipmap(target); - gl.glDisable(target); - img.setMipmapsGenerated(true); - } - } - - img.clearUpdateNeeded(); - } - - @Override - public void setTexture(int unit, Texture tex) { - GL gl = GLContext.getCurrentGL(); - Image image = tex.getImage(); - if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { - updateTexImageData(image, tex.getType(), unit); - } - - int texId = image.getId(); - assert texId != -1; - - Image[] textures = context.boundTextures; - - int type = convertTextureType(tex.getType(), image.getMultiSamples(), -1); -// if (!context.textureIndexList.moveToNew(unit)) { -// if (context.boundTextureUnit != unit){ -// gl.glActiveTexture(GL.GL_TEXTURE0 + unit); -// context.boundTextureUnit = unit; -// } -// gl.glEnable(type); -// } - - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - if (textures[unit] != image) { - gl.glBindTexture(type, texId); - textures[unit] = image; - - statistics.onTextureUse(image, true); - } else { - statistics.onTextureUse(image, false); - } - - setupTextureParams(tex); - } - - @Override - public void modifyTexture(Texture tex, Image pixels, int x, int y) { - setTexture(0, tex); - TextureUtil.uploadSubTexture(pixels, convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), -1), 0, x, y, linearizeSrgbImages); - } - - public void clearTextureUnits() { - /*GL gl = GLContext.getCurrentGL(); - IDList textureList = context.textureIndexList; - Texture[] textures = context.boundTextures; - for (int i = 0; i < textureList.oldLen; i++) { - int idx = textureList.oldList[i]; - - if (context.boundTextureUnit != idx) { - gl.glActiveTexture(GL.GL_TEXTURE0 + idx); - context.boundTextureUnit = idx; - } - gl.glDisable(convertTextureType(textures[idx].getType())); - textures[idx] = null; - } - context.textureIndexList.copyNewToOld();*/ - } - - @Override - public void deleteImage(Image image) { - int texId = image.getId(); - if (texId != -1) { - intBuf1.put(0, texId); - intBuf1.position(0).limit(1); - GL gl = GLContext.getCurrentGL(); - gl.glDeleteTextures(1, intBuf1); - image.resetObject(); - - statistics.onDeleteTexture(); - } - } - - /*********************************************************************\ - |* Vertex Buffers and Attributes *| - \*********************************************************************/ - private int convertUsage(Usage usage) { - switch (usage) { - case Static: - return GL.GL_STATIC_DRAW; - case Dynamic: - return GL.GL_DYNAMIC_DRAW; - case Stream: - return GL2ES2.GL_STREAM_DRAW; - default: - throw new RuntimeException("Unknown usage type: " + usage); - } - } - - private int convertFormat(VertexBuffer.Format format) { - switch (format) { - case Byte: - return GL.GL_BYTE; - case UnsignedByte: - return GL.GL_UNSIGNED_BYTE; - case Short: - return GL.GL_SHORT; - case UnsignedShort: - return GL.GL_UNSIGNED_SHORT; - case Int: - return GL2ES2.GL_INT; - case UnsignedInt: - return GL.GL_UNSIGNED_INT; - case Float: - return GL.GL_FLOAT; - case Double: - return GL2GL3.GL_DOUBLE; - default: - throw new UnsupportedOperationException("Unknown buffer format."); - - } - } - - @Override - public void updateBufferData(VertexBuffer vb) { - GL gl = GLContext.getCurrentGL(); - int bufId = vb.getId(); - boolean created = false; - if (bufId == -1) { - // create buffer - gl.glGenBuffers(1, intBuf1); - bufId = intBuf1.get(0); - vb.setId(bufId); - objManager.registerObject(vb); - - //statistics.onNewVertexBuffer(); - - created = true; - } - - // bind buffer - int target; - if (vb.getBufferType() == VertexBuffer.Type.Index) { - target = GL.GL_ELEMENT_ARRAY_BUFFER; - if (context.boundElementArrayVBO != bufId) { - gl.glBindBuffer(target, bufId); - context.boundElementArrayVBO = bufId; - //statistics.onVertexBufferUse(vb, true); - } else { - //statistics.onVertexBufferUse(vb, false); - } - } else { - target = GL.GL_ARRAY_BUFFER; - if (context.boundArrayVBO != bufId) { - gl.glBindBuffer(target, bufId); - context.boundArrayVBO = bufId; - //statistics.onVertexBufferUse(vb, true); - } else { - //statistics.onVertexBufferUse(vb, false); - } - } - - int usage = convertUsage(vb.getUsage()); - Buffer data = vb.getData(); - data.rewind(); - - if (created || vb.hasDataSizeChanged()) { - // upload data based on format - gl.glBufferData(target, data.capacity() * vb.getFormat().getComponentSize(), data, usage); - } else { - gl.glBufferSubData(target, 0, data.capacity() * vb.getFormat().getComponentSize(), data); - } - - vb.clearUpdateNeeded(); - } - - @Override - public void deleteBuffer(VertexBuffer vb) { - GL gl = GLContext.getCurrentGL(); - int bufId = vb.getId(); - if (bufId != -1) { - // delete buffer - intBuf1.put(0, bufId); - intBuf1.position(0).limit(1); - gl.glDeleteBuffers(1, intBuf1); - vb.resetObject(); - - //statistics.onDeleteVertexBuffer(); - } - } - - public void clearVertexAttribs() { - GL gl = GLContext.getCurrentGL(); - IDList attribList = context.attribIndexList; - for (int i = 0; i < attribList.oldLen; i++) { - int idx = attribList.oldList[i]; - gl.getGL2ES2().glDisableVertexAttribArray(idx); - if (context.boundAttribs[idx].isInstanced() && gl.isGL3ES3()) { - gl.getGL3ES3().glVertexAttribDivisor(idx, 0); - } - context.boundAttribs[idx] = null; - } - 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"); - } - - int programId = context.boundShaderProgram; - if (programId > 0) { - GL gl = GLContext.getCurrentGL(); - Attribute attrib = boundShader.getAttribute(vb.getBufferType()); - int loc = attrib.getLocation(); - if (loc == -1) { - return; // not defined - } - if (loc == -2) { - stringBuf.setLength(0); - // JOGL 2.0 doesn't need a null terminated string - stringBuf.append("in").append(vb.getBufferType().name()); - loc = gl.getGL2ES2().glGetAttribLocation(programId, stringBuf.toString()); - - // 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); - } - } - - if (vb.isInstanced()) { - if (!gl.isExtensionAvailable("GL_ARB_instanced_arrays") - || !gl.isExtensionAvailable("GL_ARB_draw_instanced")) { - 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); - } - - VertexBuffer[] attribs = context.boundAttribs; - for (int i = 0; i < slotsRequired; i++) { - if (!context.attribIndexList.moveToNew(loc + i)) { - gl.getGL2ES2().glEnableVertexAttribArray(loc + i); - //System.out.println("Enabled ATTRIB IDX: "+loc + i); - } - } - if (attribs[loc] != vb) { - // 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.getGL2ES2().glVertexAttribPointer(loc, - 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.getGL2ES2().glVertexAttribPointer(loc + i, - 4, - convertFormat(vb.getFormat()), - vb.isNormalized(), - 4 * 4 * slotsRequired, - 4 * 4 * i); - } - } - - for (int i = 0; i < slotsRequired; i++) { - int slot = loc + i; - if (vb.isInstanced() && (attribs[slot] == null || !attribs[slot].isInstanced())) { - // non-instanced -> instanced - gl.getGL3ES3().glVertexAttribDivisor(slot, vb.getInstanceSpan()); - } else if (!vb.isInstanced() && attribs[slot] != null && attribs[slot].isInstanced()) { - // instanced -> non-instanced - gl.getGL3ES3().glVertexAttribDivisor(slot, 0); - } - attribs[slot] = 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) { - boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); - GL gl = GLContext.getCurrentGL(); - if (useInstancing) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawArraysInstanced(convertElementMode(mode), 0, - vertCount, count); - } - } else { - gl.glDrawArrays(convertElementMode(mode), 0, vertCount); - } - } - - 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; - - GL gl = GLContext.getCurrentGL(); - - if (context.boundElementArrayVBO != bufId) { - gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, bufId); - context.boundElementArrayVBO = bufId; - //statistics.onVertexBufferUse(indexBuf, true); - } else { - //statistics.onVertexBufferUse(indexBuf, true); - } - - int vertCount = mesh.getVertexCount(); - boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); - - 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.TriangleStrip); - } - int elementLength = elementLengths[i]; - - if (useInstancing) { - if (gl.isGL2()) { - indexBuf.getData().position(curOffset); - indexBuf.getData().limit(curOffset + elementLength); - - gl.getGL2().glDrawElementsInstanced(elMode, - elementLength, - fmt, - indexBuf.getData(), - count); - } else { - throw new IllegalArgumentException( - "instancing is not supported."); - } - } else { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawRangeElements(elMode, - 0, - vertCount, - elementLength, - fmt, - curOffset); - } else { - indexBuf.getData().position(curOffset); - gl.getGL2().glDrawElements(elMode, elementLength, fmt, - indexBuf.getData()); - } - } - - curOffset += elementLength * elSize; - } - } else { - if (useInstancing) { - if (gl.isGL2()) { - gl.getGL2().glDrawElementsInstanced(convertElementMode(mesh.getMode()), - indexBuf.getData().limit(), - convertFormat(indexBuf.getFormat()), - indexBuf.getData(), - count); - } else { - throw new IllegalArgumentException( - "instancing is not supported."); - } - } else { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glDrawRangeElements(convertElementMode(mesh.getMode()), - 0, - vertCount, - indexBuf.getData().limit(), - convertFormat(indexBuf.getFormat()), - 0); - } else { - indexBuf.getData().rewind(); - gl.glDrawElements(convertElementMode(mesh.getMode()), - indexBuf.getData().limit(), - convertFormat(indexBuf.getFormat()), 0); - } - } - } - } - - /** - * *******************************************************************\ |* - * Render Calls *| - \******************************************************************** - */ - private int convertElementMode(Mesh.Mode mode) { - switch (mode) { - case Points: - return GL.GL_POINTS; - case Lines: - return GL.GL_LINES; - case LineLoop: - return GL.GL_LINE_LOOP; - case LineStrip: - return GL.GL_LINE_STRIP; - case Triangles: - return GL.GL_TRIANGLES; - case TriangleFan: - return GL.GL_TRIANGLE_FAN; - case TriangleStrip: - return GL.GL_TRIANGLE_STRIP; - default: - throw new UnsupportedOperationException("Unrecognized mesh mode: " + mode); - } - } - - public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) { - int id = mesh.getId(); - GL gl = GLContext.getCurrentGL(); - - if (id == -1) { - IntBuffer temp = intBuf1; - if (gl.isGL2GL3()) { - gl.getGL2GL3().glGenVertexArrays(1, temp); - } - id = temp.get(0); - mesh.setId(id); - } - - if (context.boundVertexArray != id) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glBindVertexArray(id); - } - context.boundVertexArray = id; - } - - VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); - if (interleavedData != null && interleavedData.isUpdateNeeded()) { - updateBufferData(interleavedData); - } - - if (instanceData != null) { - setVertexAttrib(instanceData, null); - } - - 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); - } - } - } - - 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 - } - - if (context.boundVertexArray != mesh.getId()) { - GL gl = GLContext.getCurrentGL(); - if (gl.isGL2GL3()) { - gl.getGL2GL3().glBindVertexArray(mesh.getId()); - } - context.boundVertexArray = mesh.getId(); - } - -// 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()); - } - clearVertexAttribs(); - clearTextureUnits(); - } - - 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); - - VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); - if (interleavedData != null && interleavedData.isUpdateNeeded()) { - updateBufferData(interleavedData); - } - - VertexBuffer indices; - if (mesh.getNumLodLevels() > 0) { - indices = mesh.getLodLevel(lod); - } else { - indices = mesh.getBuffer(Type.Index); - } - - if (instanceData != null) { - for (VertexBuffer vb : instanceData) { - setVertexAttrib(vb, null); - } - } - - 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 { - drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); - } - clearVertexAttribs(); - clearTextureUnits(); - } - - @Override - public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { - if (mesh.getVertexCount() == 0) { - return; - } - - GL gl = GLContext.getCurrentGL(); - 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 (gl.isGL2ES1()) { - gl.glDisable(GL2ES1.GL_POINT_SPRITE); - } - if (gl.isGL2GL3()) { - gl.glDisable(GL2GL3.GL_VERTEX_PROGRAM_POINT_SIZE); - } - context.pointSprite = false; - } - } - - if (context.pointSize != mesh.getPointSize()) { - if (gl.isGL2GL3()) { - gl.getGL2GL3().glPointSize(mesh.getPointSize()); - } - context.pointSize = mesh.getPointSize(); - } - if (context.lineWidth != mesh.getLineWidth()) { - gl.glLineWidth(mesh.getLineWidth()); - context.lineWidth = mesh.getLineWidth(); - } - - statistics.onMeshDrawn(mesh, lod); -// if (gl.isExtensionAvailable("GL_ARB_vertex_array_object")){ -// renderMeshVertexArray(mesh, lod, count); -// }else{ - renderMeshDefault(mesh, lod, count, instanceData); -// } - } - - @Override - public void setMainFrameBufferSrgb(boolean srgb) { - //Gamma correction - if(srgb && caps.contains(Caps.Srgb)){ - GLContext.getCurrentGL().glEnable(GL3.GL_FRAMEBUFFER_SRGB); - logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)"); - }else{ - GLContext.getCurrentGL().glDisable(GL3.GL_FRAMEBUFFER_SRGB); - } - - } - - @Override - public void setLinearizeSrgbImages(boolean linearize) { - linearizeSrgbImages = linearize; - } - - public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { - throw new UnsupportedOperationException("Not supported yet. URA will make that work seamlessly"); - } -} 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; - } - } -} From 3d2a9b83e9834c596177e17332f650d5f4ba8157 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 12 Sep 2015 17:51:19 -0400 Subject: [PATCH 12/15] JOGL: fix syntax error due to missing renderer --- .../src/main/java/com/jme3/system/jogl/JoglContext.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 3b90877df..1b0483066 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 @@ -40,14 +40,12 @@ import com.jme3.renderer.RendererException; import com.jme3.renderer.jogl.JoglGL; import com.jme3.renderer.jogl.JoglGLExt; import com.jme3.renderer.jogl.JoglGLFbo; -import com.jme3.renderer.jogl.JoglRenderer; 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; @@ -55,6 +53,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; @@ -194,7 +193,7 @@ public abstract class JoglContext implements JmeContext { //FIXME uncomment the line below when the unified renderer is ready for the prime time :) //renderer = new GLRenderer(gl, glext, glfbo); - renderer = new JoglRenderer(); + renderer = new NullRenderer(); renderer.initialize(); } else { throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer()); From 28e2b5650c65a70bdc7acfde035a285235caa86a Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 12 Sep 2015 17:51:42 -0400 Subject: [PATCH 13/15] GLRenderer: disable global VAO, since VAO is now supported --- .../src/main/java/com/jme3/renderer/opengl/GLRenderer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 8b6a307be..14da67de7 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 @@ -511,9 +511,9 @@ public 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); From 4e572605a8d195cf7f8dfe3b73be4b8f2086067e Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 12 Sep 2015 17:52:56 -0400 Subject: [PATCH 14/15] GLRenderer: merge changes from master --- .../com/jme3/renderer/opengl/GLRenderer.java | 126 ++++++++++-------- 1 file changed, 74 insertions(+), 52 deletions(-) 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 14da67de7..132fb4cd9 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 @@ -1816,7 +1816,7 @@ public class GLRenderer implements Renderer { } @SuppressWarnings("fallthrough") - private void setupTextureParams(Texture tex) { + private void setupTextureParams(int unit, Texture tex) { Image image = tex.getImage(); int target = convertTextureType(tex.getType(), image != null ? image.getMultiSamples() : 1, -1); @@ -1829,32 +1829,23 @@ public class GLRenderer implements Renderer { // filter things if (image.getLastTextureState().magFilter != tex.getMagFilter()) { int magFilter = convertMagFilter(tex.getMagFilter()); + bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter); image.getLastTextureState().magFilter = tex.getMagFilter(); } if (image.getLastTextureState().minFilter != tex.getMinFilter()) { int minFilter = convertMinFilter(tex.getMinFilter(), haveMips); + bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter); image.getLastTextureState().minFilter = tex.getMinFilter(); } - if (caps.contains(Caps.SeamlessCubemap) && tex.getType() == Texture.Type.CubeMap) { - if (haveMips && !context.seamlessCubemap) { - // We can enable seamless cubemap filtering. - gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS); - context.seamlessCubemap = true; - } else if (!haveMips && context.seamlessCubemap) { - // For skyboxes (no mipmaps), disable seamless cubemap filtering. - gl.glDisable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS); - context.seamlessCubemap = false; - } - } - - if (tex.getAnisotropicFilter() > 1) { - if (caps.contains(Caps.TextureFilterAnisotropic)) { - gl.glTexParameterf(target, - GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, - tex.getAnisotropicFilter()); - } + if (caps.contains(Caps.TextureFilterAnisotropic) + && image.getLastTextureState().anisoFilter != tex.getAnisotropicFilter()) { + bindTextureAndUnit(target, image, unit); + gl.glTexParameterf(target, + GLExt.GL_TEXTURE_MAX_ANISOTROPY_EXT, + tex.getAnisotropicFilter()); + image.getLastTextureState().anisoFilter = tex.getAnisotropicFilter(); } // repeat modes @@ -1862,6 +1853,7 @@ public class GLRenderer implements Renderer { case ThreeDimensional: case CubeMap: // cubemaps use 3D coords if (gl2 != null && image.getLastTextureState().rWrap != tex.getWrap(WrapAxis.R)) { + bindTextureAndUnit(target, image, unit); gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R))); image.getLastTextureState().rWrap = tex.getWrap(WrapAxis.R); } @@ -1869,10 +1861,12 @@ public class GLRenderer implements Renderer { case TwoDimensional: case TwoDimensionalArray: if (image.getLastTextureState().tWrap != tex.getWrap(WrapAxis.T)) { + bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, convertWrapMode(tex.getWrap(WrapAxis.T))); image.getLastTextureState().tWrap = tex.getWrap(WrapAxis.T); } if (image.getLastTextureState().sWrap != tex.getWrap(WrapAxis.S)) { + bindTextureAndUnit(target, image, unit); gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); image.getLastTextureState().sWrap = tex.getWrap(WrapAxis.S); } @@ -1881,21 +1875,27 @@ public class GLRenderer implements Renderer { throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); } - if(tex.isNeedCompareModeUpdate() && gl2 != null){ + if (tex.isNeedCompareModeUpdate() && gl2 != null) { // R to Texture compare mode if (tex.getShadowCompareMode() != Texture.ShadowCompareMode.Off) { + bindTextureAndUnit(target, image, unit); gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_R_TO_TEXTURE); + gl2.glTexParameteri(target, GL2.GL_DEPTH_TEXTURE_MODE, GL2.GL_INTENSITY); if (tex.getShadowCompareMode() == Texture.ShadowCompareMode.GreaterOrEqual) { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL); } else { gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL); } - }else{ + } else { + bindTextureAndUnit(target, image, unit); //restoring default value gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE); } tex.compareModeUpdated(); } + + // If at this point we didn't bind the texture, bind it now + bindTextureOnly(target, image, unit); } /** @@ -1953,6 +1953,50 @@ public 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. + */ + private void bindTextureAndUnit(int target, Image img, int unit) { + if (context.boundTextureUnit != unit) { + gl.glActiveTexture(GL.GL_TEXTURE0 + unit); + context.boundTextureUnit = unit; + } + if (context.boundTextures[unit] != img) { + gl.glBindTexture(target, img.getId()); + context.boundTextures[unit] = img; + statistics.onTextureUse(img, true); + } else { + 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. + */ + private void bindTextureOnly(int target, Image img, int unit) { + if (context.boundTextures[unit] != img) { + if (context.boundTextureUnit != unit) { + gl.glActiveTexture(GL.GL_TEXTURE0 + unit); + context.boundTextureUnit = unit; + } + gl.glBindTexture(target, img.getId()); + context.boundTextures[unit] = img; + statistics.onTextureUse(img, true); + } else { + statistics.onTextureUse(img, false); + } + } + /** * Uploads the given image to the GL driver. * @@ -1974,19 +2018,10 @@ public class GLRenderer implements Renderer { statistics.onNewTexture(); } - // bind texture + // bind texture int target = convertTextureType(type, img.getMultiSamples(), -1); - if (context.boundTextures[unit] != img) { - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - - gl.glBindTexture(target, texId); - context.boundTextures[unit] = img; - - statistics.onTextureUse(img, true); - } + + bindTextureAndUnit(target, img, unit); if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) { // Image does not have mipmaps, but they are required. @@ -2095,6 +2130,7 @@ public class GLRenderer implements Renderer { img.clearUpdateNeeded(); } + @Override public void setTexture(int unit, Texture tex) { Image image = tex.getImage(); if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { @@ -2121,24 +2157,7 @@ public class GLRenderer implements Renderer { int texId = image.getId(); assert texId != -1; - Image[] textures = context.boundTextures; - - int type = convertTextureType(tex.getType(), image.getMultiSamples(), -1); - if (textures[unit] != image) { - if (context.boundTextureUnit != unit) { - gl.glActiveTexture(GL.GL_TEXTURE0 + unit); - context.boundTextureUnit = unit; - } - - gl.glBindTexture(type, texId); - textures[unit] = image; - - statistics.onTextureUse(image, true); - } else { - statistics.onTextureUse(image, false); - } - - setupTextureParams(tex); + setupTextureParams(unit, tex); } public void modifyTexture(Texture tex, Image pixels, int x, int y) { @@ -2722,12 +2741,15 @@ public class GLRenderer implements Renderer { 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()); } - clearVertexAttribs(); + } public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { From 0d3ebf75bdeee2593d7e4a34ee9fff2b6de54d36 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Sat, 12 Sep 2015 17:53:18 -0400 Subject: [PATCH 15/15] GLRenderer: fix NPE when using mesh without index buffer --- .../src/main/java/com/jme3/renderer/opengl/GLRenderer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 132fb4cd9..f5cb0d1b8 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 @@ -2715,7 +2715,9 @@ public class GLRenderer implements Renderer { VertexBuffer indices = getIndexBuffer(mesh, lod); if (mesh.isUpdateNeeded()) { setupVertexBuffers(mesh, instanceData); - updateBufferData(indices); + if (indices != null) { + updateBufferData(indices); + } } else { updateVertexBuffers(mesh, instanceData); if (indices != null) {