2
0
Эх сурвалжийг харах

Renderer texture handling changes
* Relax NPOT texture restrictions on OpenGL ES 2:
allow non mip-mapped, non repeating NPOT textures - mainly used for GUI elements
* Fix various texture array issues:
- compressed textures were causing a GL error
- the array size was always set to 1 instead of the actual number of images in the array

shadowislord 10 жил өмнө
parent
commit
5bfc5b2c13

+ 16 - 1
jme3-core/src/main/java/com/jme3/renderer/Caps.java

@@ -301,7 +301,22 @@ public enum Caps {
     /**
      * Supports 32-bit index buffers.
      */
-    IntegerIndexBuffer;
+    IntegerIndexBuffer,
+    
+    /**
+     * Partial support for non-power-of-2 textures, typically found
+     * on OpenGL ES 2 devices.
+     * <p>
+     * Use of NPOT textures is allowed iff:
+     * <ul>
+     * <li>The {@link Texture.WrapMode} is set to 
+     * {@link Texture.WrapMode#EdgeClamp}.</li>
+     * <li>Mip-mapping is not used, meaning {@link Texture.MinFilter} is set to
+     * {@link Texture.MinFilter#BilinearNoMipMaps} or 
+     * {@link Texture.MinFilter#NearestNoMipMaps}</li>
+     * </ul>
+     */
+    PartialNonPowerOfTwoTextures;
 
     /**
      * Returns true if given the renderer capabilities, the texture

+ 10 - 8
jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormat.java

@@ -44,7 +44,7 @@ public final class GLImageFormat {
     public final boolean compressed;
 
     /**
-     * Constructor for uncompressed formats.
+     * Constructor for formats.
      * 
      * @param internalFormat OpenGL internal format
      * @param format OpenGL format
@@ -58,14 +58,16 @@ public final class GLImageFormat {
     }
     
     /**
-     * Constructor for compressed formats.
+     * Constructor for formats.
      * 
-     * @param compressedFormat OpenGL compressed internal format
+     * @param internalFormat OpenGL internal format
+     * @param format OpenGL format
+     * @param dataType OpenGL datatype
      */
-    public GLImageFormat(int compressedFormat) {
-        this.internalFormat = compressedFormat;
-        this.format = -1;
-        this.dataType = -1;
-        this.compressed = true;
+    public GLImageFormat(int internalFormat, int format, int dataType, boolean compressed) {
+        this.internalFormat = internalFormat;
+        this.format = format;
+        this.dataType = dataType;
+        this.compressed = compressed;
     }
 }

+ 18 - 14
jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java

@@ -61,14 +61,18 @@ public final class GLImageFormats {
     }
     
     private static void formatComp(GLImageFormat[][] formatToGL, Image.Format format, 
-                               int glCompressedFormat){
-        formatToGL[0][format.ordinal()] = new GLImageFormat(glCompressedFormat);
+                                   int glCompressedFormat,
+                                   int glFormat, 
+                                   int glDataType){
+        formatToGL[0][format.ordinal()] = new GLImageFormat(glCompressedFormat, glFormat, glDataType, true);
     }
     
     private static void formatCompSrgb(GLImageFormat[][] formatToGL, Image.Format format, 
-                                   int glCompressedFormat)
+                                       int glCompressedFormat,
+                                       int glFormat, 
+                                       int glDataType)
     {
-        formatToGL[1][format.ordinal()] = new GLImageFormat(glCompressedFormat);
+        formatToGL[1][format.ordinal()] = new GLImageFormat(glCompressedFormat, glFormat, glDataType, true);
     }
     
     /**
@@ -112,10 +116,10 @@ public final class GLImageFormats {
                 formatSrgb(formatToGL, Format.BGRA8,            GLExt.GL_SRGB8_ALPHA8_EXT,       GL2.GL_BGRA,           GL.GL_UNSIGNED_BYTE);
                 
                 if (caps.contains(Caps.TextureCompressionS3TC)) {
-                    formatCompSrgb(formatToGL, Format.DXT1,  GLExt.GL_COMPRESSED_SRGB_S3TC_DXT1_EXT);
-                    formatCompSrgb(formatToGL, Format.DXT1A, GLExt.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT);
-                    formatCompSrgb(formatToGL, Format.DXT3,  GLExt.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT);
-                    formatCompSrgb(formatToGL, Format.DXT5,  GLExt.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT);
+                    formatCompSrgb(formatToGL, Format.DXT1,  GLExt.GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
+                    formatCompSrgb(formatToGL, Format.DXT1A, GLExt.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
+                    formatCompSrgb(formatToGL, Format.DXT3,  GLExt.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
+                    formatCompSrgb(formatToGL, Format.DXT5,  GLExt.GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
                 }
             }
         } else if (caps.contains(Caps.Rgba8)) {
@@ -179,16 +183,16 @@ public final class GLImageFormats {
         
         // Compressed formats
         if (caps.contains(Caps.TextureCompressionS3TC)) {
-            formatComp(formatToGL, Format.DXT1,  GLExt.GL_COMPRESSED_RGB_S3TC_DXT1_EXT);
-            formatComp(formatToGL, Format.DXT1A, GLExt.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT);
-            formatComp(formatToGL, Format.DXT3,  GLExt.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT);
-            formatComp(formatToGL, Format.DXT5,  GLExt.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT);
+            formatComp(formatToGL, Format.DXT1,  GLExt.GL_COMPRESSED_RGB_S3TC_DXT1_EXT,  GL.GL_RGB,  GL.GL_UNSIGNED_BYTE);
+            formatComp(formatToGL, Format.DXT1A, GLExt.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
+            formatComp(formatToGL, Format.DXT3,  GLExt.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
+            formatComp(formatToGL, Format.DXT5,  GLExt.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE);
         }
         
         if (caps.contains(Caps.TextureCompressionETC2)) {
-            formatComp(formatToGL, Format.ETC1, GLExt.GL_COMPRESSED_RGB8_ETC2);
+            formatComp(formatToGL, Format.ETC1, GLExt.GL_COMPRESSED_RGB8_ETC2, GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
         } else if (caps.contains(Caps.TextureCompressionETC1)) {
-            formatComp(formatToGL, Format.ETC1, GLExt.GL_ETC1_RGB8_OES);
+            formatComp(formatToGL, Format.ETC1, GLExt.GL_ETC1_RGB8_OES,        GL.GL_RGB, GL.GL_UNSIGNED_BYTE);
         }
         
         return formatToGL;

+ 70 - 7
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -31,7 +31,6 @@
  */
 package com.jme3.renderer.opengl;
 
-import com.jme3.light.LightList;
 import com.jme3.material.RenderState;
 import com.jme3.material.RenderState.StencilOperation;
 import com.jme3.material.RenderState.TestFunction;
@@ -116,7 +115,7 @@ public class GLRenderer implements Renderer {
         this.gl3 = gl instanceof GL3 ? (GL3)gl : null;
         this.glfbo = glfbo;
         this.glext = glfbo instanceof GLExt ? (GLExt)glfbo : null;
-        this.texUtil = new TextureUtil(gl, gl2, glext);
+        this.texUtil = new TextureUtil(gl, gl2, glext, context);
     }
 
     @Override
@@ -359,6 +358,11 @@ public class GLRenderer implements Renderer {
                                     + "support non-power-of-2 textures. "
                                     + "Some features might not work.");
         }
+        
+        if (caps.contains(Caps.OpenGLES20)) {
+            // OpenGL ES 2 has some limited support for NPOT textures
+            caps.add(Caps.PartialNonPowerOfTwoTextures);
+        }
 
         if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30)) {
             caps.add(Caps.TextureArray);
@@ -454,6 +458,9 @@ public class GLRenderer implements Renderer {
     @SuppressWarnings("fallthrough")
     public void initialize() {
         loadCapabilities();
+        
+        // Initialize default state..
+        gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
     }
 
     public void invalidateState() {
@@ -1383,6 +1390,9 @@ public class GLRenderer implements Renderer {
         Texture tex = rb.getTexture();
         Image image = tex.getImage();
         if (image.isUpdateNeeded()) {
+            // Check NPOT requirements
+            checkNonPowerOfTwo(tex);
+            
             updateTexImageData(image, tex.getType(), 0);
 
             // NOTE: For depth textures, sets nearest/no-mips mode
@@ -1843,6 +1853,61 @@ public class GLRenderer implements Renderer {
         }
     }
 
+    /**
+     * Validates if a potentially NPOT texture is supported by the hardware.
+     * <p>
+     * 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.
+     * 
+     * @param tex The texture to validate.
+     * @throws RendererException If the texture is not supported by the hardware
+     */
+    private void checkNonPowerOfTwo(Texture tex) {
+        if (!tex.getImage().isNPOT()) {
+            // Texture is power-of-2, safe to use.
+            return;
+        }
+        
+        if (caps.contains(Caps.NonPowerOfTwoTextures)) {
+            // Texture is NPOT but it is supported by video hardware.
+            return;
+        }
+        
+        // Maybe we have some / partial support for NPOT?
+        if (!caps.contains(Caps.PartialNonPowerOfTwoTextures)) {
+            // Cannot use any type of NPOT texture (uncommon)
+            throw new RendererException("non-power-of-2 textures are not "
+                                      + "supported by the video hardware");
+        }
+        
+        // Partial NPOT supported..
+        if (tex.getMinFilter().usesMipMapLevels()) {
+            throw new RendererException("non-power-of-2 textures with mip-maps "
+                                      + "are not supported by the video hardware");
+        }
+
+        switch (tex.getType()) {
+            case CubeMap:
+            case ThreeDimensional:
+                if (tex.getWrap(WrapAxis.R) != Texture.WrapMode.EdgeClamp) {
+                    throw new RendererException("repeating non-power-of-2 textures "
+                                              + "are not supported by the video hardware");
+                }
+                // fallthrough intentional!!!
+            case TwoDimensionalArray:
+            case TwoDimensional:
+                if (tex.getWrap(WrapAxis.S) != Texture.WrapMode.EdgeClamp
+                        || tex.getWrap(WrapAxis.T) != Texture.WrapMode.EdgeClamp) {
+                    throw new RendererException("repeating non-power-of-2 textures "
+                                              + "are not supported by the video hardware");
+                }
+                break;
+            default:
+                throw new UnsupportedOperationException("unrecongized texture type");
+        }
+    }
+    
     /**
      * Uploads the given image to the GL driver.
      * 
@@ -1905,11 +1970,6 @@ public class GLRenderer implements Renderer {
             }
         }
 
-        // Yes, some OpenGL2 cards (GeForce 5) still dont support NPOT.
-        if (!caps.contains(Caps.NonPowerOfTwoTextures) && img.isNPOT()) {
-            throw new RendererException("non-power-of-2 framebuffer textures are not supported by the video hardware");
-        }
-
         // Check if graphics card doesn't support multisample textures
         if (!caps.contains(Caps.TextureMultisample)) {
             if (img.getMultiSamples() > 1) {
@@ -1984,6 +2044,9 @@ public class GLRenderer implements Renderer {
     public void setTexture(int unit, Texture tex) {
         Image image = tex.getImage();
         if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) {
+            // Check NPOT requirements
+            checkNonPowerOfTwo(tex);
+            
             updateTexImageData(image, tex.getType(), unit);
         }
 

+ 10 - 10
jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java

@@ -32,6 +32,7 @@
 package com.jme3.renderer.opengl;
 
 import com.jme3.renderer.Caps;
+import com.jme3.renderer.RenderContext;
 import com.jme3.renderer.RendererException;
 import com.jme3.texture.Image;
 import com.jme3.texture.Image.Format;
@@ -53,12 +54,14 @@ final class TextureUtil {
     private final GL gl;
     private final GL2 gl2;
     private final GLExt glext;
+    private final RenderContext context;
     private GLImageFormat[][] formats;
 
-    public TextureUtil(GL gl, GL2 gl2, GLExt glext) {
+    public TextureUtil(GL gl, GL2 gl2, GLExt glext, RenderContext context) {
         this.gl = gl;
         this.gl2 = gl2;
         this.glext = glext;
+        this.context = context;
     }
     
     public void initialize(EnumSet<Caps> caps) {
@@ -210,24 +213,21 @@ final class TextureUtil {
         Image.Format jmeFormat = image.getFormat();
         GLImageFormat oglFormat = getImageFormatWithError(jmeFormat, getSrgbFormat);
 
-        ByteBuffer data;
+        ByteBuffer data = null;
         int sliceCount = 1;
-        if (index >= 0 && image.getData() != null && image.getData().size() > 0) {
+        
+        if (index >= 0) {
             data = image.getData(index);
+        }
+        
+        if (image.getData() != null && image.getData().size() > 0) {
             sliceCount = image.getData().size();
-        } else {
-            data = null;
         }
 
         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