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
This commit is contained in:
parent
fb6fb73239
commit
5bfc5b2c13
@ -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…
x
Reference in New Issue
Block a user