diff --git a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java index 58dc87550..7036418e8 100644 --- a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java +++ b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java @@ -138,6 +138,11 @@ class BitmapTextPage extends Geometry { Mesh m = getMesh(); int vertCount = pageQuads.size() * 4; int triCount = pageQuads.size() * 2; + + if (vertCount > m.getVertexCount() || + triCount > m.getTriangleCount()) { + m.setUpdateNeeded(); + } VertexBuffer pb = m.getBuffer(Type.Position); VertexBuffer tb = m.getBuffer(Type.TexCoord); diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index edfd380b4..1784eb7f5 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -283,6 +283,12 @@ public interface Renderer { * the per-instance attributes. */ public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData); + + /** + * Delete a Mesh (or Vertex Array Object in GL terms) from the GPU. + * @param mesh The mesh to delete. + */ + public void deleteMesh(Mesh mesh); /** * Resets all previously used {@link NativeObject Native Objects} on this Renderer. diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 6576095a5..aad9e1cd6 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -149,7 +149,7 @@ public final class GLRenderer implements Renderer { int major = Integer.parseInt(m.group(1)); int minor = Integer.parseInt(m.group(2)); if (minor >= 10 && minor % 10 == 0) { - // some versions can look like "1.30" instead of "1.3". + // some versions can look like "1.30" instead of "1.3". // make sure to correct for this minor /= 10; } @@ -379,7 +379,7 @@ public final class GLRenderer implements Renderer { limits.put(Limits.TextureAnisotropy, getInteger(GLExt.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)); } - if (hasExtension("GL_EXT_framebuffer_object") + if (hasExtension("GL_EXT_framebuffer_object") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES20)) { caps.add(Caps.FrameBuffer); @@ -474,7 +474,7 @@ public final class GLRenderer implements Renderer { { sb.append("\t").append(cap.toString()).append("\n"); } - + sb.append("\nHardware limits: \n"); for (Limits limit : Limits.values()) { Integer value = limits.get(limit); @@ -484,7 +484,7 @@ public final class GLRenderer implements Renderer { sb.append("\t").append(limit.name()).append(" = ") .append(value).append("\n"); } - + logger.log(Level.INFO, sb.toString()); } @@ -517,7 +517,7 @@ public final class GLRenderer implements Renderer { // Initialize default state.. gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); - + if (caps.contains(Caps.SeamlessCubemap)) { // Enable this globally. Should be OK. gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS); @@ -525,9 +525,9 @@ public final class GLRenderer implements Renderer { if (caps.contains(Caps.CoreProfile)) { // Core Profile requires VAO to be bound. - gl3.glGenVertexArrays(intBuf16); - int vaoId = intBuf16.get(0); - gl3.glBindVertexArray(vaoId); +// gl3.glGenVertexArrays(intBuf16); +// int vaoId = intBuf16.get(0); +// gl3.glBindVertexArray(vaoId); } if (gl2 != null) { gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); @@ -637,7 +637,7 @@ public final class GLRenderer implements Renderer { gl.glDepthFunc(convertTestFunction(state.getDepthFunc())); context.depthFunc = state.getDepthFunc(); } - + if (state.isDepthWrite() && !context.depthWriteEnabled) { gl.glDepthMask(true); context.depthWriteEnabled = true; @@ -971,12 +971,12 @@ public final class GLRenderer implements Renderer { gl.glUniform1i(loc, b.booleanValue() ? GL.GL_TRUE : GL.GL_FALSE); break; case Matrix3: - fb = (FloatBuffer) uniform.getValue(); + fb = uniform.getMultiData(); assert fb.remaining() == 9; gl.glUniformMatrix3(loc, false, fb); break; case Matrix4: - fb = (FloatBuffer) uniform.getValue(); + fb = uniform.getMultiData(); assert fb.remaining() == 16; gl.glUniformMatrix4(loc, false, fb); break; @@ -985,23 +985,23 @@ public final class GLRenderer implements Renderer { gl.glUniform1(loc, ib); break; case FloatArray: - fb = (FloatBuffer) uniform.getValue(); + fb = uniform.getMultiData(); gl.glUniform1(loc, fb); break; case Vector2Array: - fb = (FloatBuffer) uniform.getValue(); + fb = uniform.getMultiData(); gl.glUniform2(loc, fb); break; case Vector3Array: - fb = (FloatBuffer) uniform.getValue(); + fb = uniform.getMultiData(); gl.glUniform3(loc, fb); break; case Vector4Array: - fb = (FloatBuffer) uniform.getValue(); + fb = uniform.getMultiData(); gl.glUniform4(loc, fb); break; case Matrix4Array: - fb = (FloatBuffer) uniform.getValue(); + fb = uniform.getMultiData(); gl.glUniformMatrix4(loc, false, fb); break; case Int: @@ -1086,7 +1086,7 @@ public final class GLRenderer implements Renderer { if (gles2) { // request GLSL ES (1.00) when compiling under GLES2. stringBuf.append("#version 100\n"); - + if (source.getType() == ShaderType.Fragment) { // GLES2 requires precision qualifier. stringBuf.append("precision mediump float;\n"); @@ -1473,7 +1473,7 @@ public final class GLRenderer implements Renderer { rb.getId()); } } - + private void bindFrameBuffer(FrameBuffer fb) { if (fb == null) { if (context.boundFBO != 0) { @@ -1511,12 +1511,12 @@ public final class GLRenderer implements Renderer { } bindFrameBuffer(fb); - + for (int i = 0; i < fb.getNumColorBuffers(); i++) { FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i); updateFrameBufferAttachment(fb, colorBuf); } - + FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer(); if (depthBuf != null) { updateFrameBufferAttachment(fb, depthBuf); @@ -1562,11 +1562,11 @@ public final class GLRenderer implements Renderer { if (gl2 == null) { return; } - + final int NONE = -2; final int INITIAL = -1; final int MRT_OFF = 100; - + if (fb == null) { // Set Read/Draw buffers to initial value. if (context.boundDrawBuf != INITIAL) { @@ -1630,9 +1630,9 @@ public final class GLRenderer implements Renderer { } } } - + } - + public void setFrameBuffer(FrameBuffer fb) { if (fb == null && mainFbOverride != null) { fb = mainFbOverride; @@ -1858,7 +1858,7 @@ public final class GLRenderer implements Renderer { if (image != null) { haveMips = image.isGeneratedMipmapsRequired() || image.hasMipmaps(); } - + LastTextureState curState = image.getLastTextureState(); if (curState.magFilter != tex.getMagFilter()) { @@ -1921,7 +1921,7 @@ public final class GLRenderer implements Renderer { } curState.shadowCompareMode = texCompareMode; } - + // If at this point we didn't bind the texture, bind it now bindTextureOnly(target, image, unit); } @@ -1929,7 +1929,7 @@ public final class GLRenderer implements Renderer { /** * Validates if a potentially NPOT texture is supported by the hardware. *

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