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
experimental
shadowislord 10 years ago
parent fb6fb73239
commit 5bfc5b2c13
  1. 17
      jme3-core/src/main/java/com/jme3/renderer/Caps.java
  2. 18
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormat.java
  3. 32
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java
  4. 77
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  5. 20
      jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.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

@ -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;
}
}

@ -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;

@ -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);
}

@ -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

Loading…
Cancel
Save