From f68475319af7ea6317e9efa5cfc0003b35852669 Mon Sep 17 00:00:00 2001 From: "Sha..rd" Date: Sat, 14 Apr 2012 19:26:22 +0000 Subject: [PATCH] * Added new image format: Depth24Stencil8, for framebuffers and textures that need stencil data * Renderer now uses getArray() to access mesh's buffers * GL image format data now stored in a special data structure for easier management (instead of huge switch/case statement) * Android texture loading: If the format is not supported by GLES spec, the texture upload will fail git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9298 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../jme3/renderer/android/TextureUtil.java | 136 +--- .../jme3/renderer/lwjgl/LwjglGL1Renderer.java | 15 +- .../jme3/renderer/lwjgl/LwjglRenderer.java | 56 +- .../com/jme3/renderer/lwjgl/TextureUtil.java | 606 +++++++----------- 4 files changed, 275 insertions(+), 538 deletions(-) diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java index 53b96b400..4137f37e0 100644 --- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java +++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java @@ -6,50 +6,11 @@ import android.opengl.GLUtils; import com.jme3.asset.AndroidImageInfo; import com.jme3.math.FastMath; import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; import java.nio.ByteBuffer; import javax.microedition.khronos.opengles.GL10; public class TextureUtil { - public static int convertTextureFormat(Format fmt){ - switch (fmt){ - case Alpha16: - case Alpha8: - return GL10.GL_ALPHA; - case Luminance8Alpha8: - case Luminance16Alpha16: - return GL10.GL_LUMINANCE_ALPHA; - case Luminance8: - case Luminance16: - return GL10.GL_LUMINANCE; - case RGB10: - case RGB16: - case BGR8: - case RGB8: - case RGB565: - return GL10.GL_RGB; - case RGB5A1: - case RGBA16: - case RGBA8: - return GL10.GL_RGBA; - - case Depth: - return GLES20.GL_DEPTH_COMPONENT; - case Depth16: - return GLES20.GL_DEPTH_COMPONENT16; - case Depth24: - case Depth32: - case Depth32F: - throw new UnsupportedOperationException("Unsupported depth format: " + fmt); - - case DXT1A: - throw new UnsupportedOperationException("Unsupported format: " + fmt); - default: - throw new UnsupportedOperationException("Unrecognized format: " + fmt); - } - } - private static void buildMipmap(Bitmap bitmap) { int level = 0; int height = bitmap.getHeight(); @@ -77,21 +38,17 @@ public class TextureUtil { /** * uploadTextureBitmap uploads a native android bitmap - * @param target - * @param bitmap - * @param generateMips - * @param powerOf2 */ - public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2) - { - if (!powerOf2) - { + public static void uploadTextureBitmap(final int target, Bitmap bitmap, boolean generateMips, boolean powerOf2) { + if (!powerOf2) { + // Power of 2 images are not supported by this GPU. int width = bitmap.getWidth(); int height = bitmap.getHeight(); - if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) - { - // scale to power of two - width = FastMath.nearestPowerOfTwo(width); + + // If the image is not power of 2, rescale it + if (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)) { + // scale to power of two, then recycle the old image. + width = FastMath.nearestPowerOfTwo(width); height = FastMath.nearestPowerOfTwo(height); Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); bitmap.recycle(); @@ -99,19 +56,15 @@ public class TextureUtil { } } - if (generateMips) - { + if (generateMips) { buildMipmap(bitmap); - } - else - { + } else { GLUtils.texImage2D(target, 0, bitmap, 0); //bitmap.recycle(); } } - public static void uploadTexture( - Image img, + public static void uploadTexture(Image img, int target, int index, int border, @@ -128,7 +81,6 @@ public class TextureUtil { // Otherwise upload image directly. // Prefer to only use power of 2 textures here to avoid errors. - Image.Format fmt = img.getFormat(); ByteBuffer data; if (index >= 0 || img.getData() != null && img.getData().size() > 0){ @@ -142,12 +94,20 @@ public class TextureUtil { int depth = img.getDepth(); boolean compress = false; - int internalFormat = -1; int format = -1; int dataType = -1; switch (fmt){ + case RGBA16: + case RGB16: + case RGB10: + case Luminance16: + case Luminance16Alpha16: case Alpha16: + case Depth32: + case Depth32F: + throw new UnsupportedOperationException("The image format '" + + fmt + "' is not supported by OpenGL ES 2.0 specification."); case Alpha8: format = GLES20.GL_ALPHA; dataType = GLES20.GL_UNSIGNED_BYTE; @@ -160,34 +120,16 @@ public class TextureUtil { format = GLES20.GL_LUMINANCE_ALPHA; dataType = GLES20.GL_UNSIGNED_BYTE; break; - case Luminance16Alpha16: - format = GLES20.GL_LUMINANCE_ALPHA; - dataType = GLES20.GL_UNSIGNED_BYTE; - break; - case Luminance16: - format = GLES20.GL_LUMINANCE; - dataType = GLES20.GL_UNSIGNED_BYTE; - break; case RGB565: format = GLES20.GL_RGB; - internalFormat = GLES20.GL_RGB565; dataType = GLES20.GL_UNSIGNED_SHORT_5_6_5; break; case ARGB4444: - format = GLES20.GL_RGBA; + format = GLES20.GL_RGBA4; dataType = GLES20.GL_UNSIGNED_SHORT_4_4_4_4; break; - case RGB10: - format = GLES20.GL_RGB; - dataType = GLES20.GL_UNSIGNED_BYTE; - break; - case RGB16: - format = GLES20.GL_RGB; - dataType = GLES20.GL_UNSIGNED_BYTE; - break; case RGB5A1: format = GLES20.GL_RGBA; - internalFormat = GLES20.GL_RGB5_A1; dataType = GLES20.GL_UNSIGNED_SHORT_5_5_5_1; break; case RGB8: @@ -198,42 +140,23 @@ public class TextureUtil { format = GLES20.GL_RGB; dataType = GLES20.GL_UNSIGNED_BYTE; break; - case RGBA16: - format = GLES20.GL_RGBA; - internalFormat = GLES20.GL_RGBA4; - dataType = GLES20.GL_UNSIGNED_BYTE; - break; case RGBA8: format = GLES20.GL_RGBA; dataType = GLES20.GL_UNSIGNED_BYTE; break; - case DXT1A: - format = GLES20.GL_COMPRESSED_TEXTURE_FORMATS; - dataType = GLES20.GL_UNSIGNED_BYTE; case Depth: - format = GLES20.GL_DEPTH_COMPONENT; - dataType = GLES20.GL_UNSIGNED_BYTE; - break; case Depth16: + case Depth24: format = GLES20.GL_DEPTH_COMPONENT; - internalFormat = GLES20.GL_DEPTH_COMPONENT16; dataType = GLES20.GL_UNSIGNED_BYTE; break; - case Depth24: - case Depth32: - case Depth32F: - throw new UnsupportedOperationException("Unsupported depth format: " + fmt); default: throw new UnsupportedOperationException("Unrecognized format: " + fmt); } - - if (internalFormat == -1) - { - internalFormat = format; - } - if (data != null) + if (data != null) { GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); + } int[] mipSizes = img.getMipMapSizes(); int pos = 0; @@ -246,9 +169,10 @@ public class TextureUtil { // XXX: might want to change that when support // of more than paletted compressions is added.. + /// NOTE: Doesn't support mipmaps if (compress){ data.clear(); - GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, + GLES20.glCompressedTexImage2D(target, 1 - mipSizes.length, format, width, @@ -262,7 +186,7 @@ public class TextureUtil { 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); +// int mipDepth = Math.max(1, depth >> i); if (data != null){ data.position(pos); @@ -270,7 +194,7 @@ public class TextureUtil { } if (compress && data != null){ - GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, + GLES20.glCompressedTexImage2D(target, i, format, mipWidth, @@ -279,9 +203,9 @@ public class TextureUtil { data.remaining(), data); }else{ - GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, + GLES20.glTexImage2D(target, i, - internalFormat, + format, mipWidth, mipHeight, 0, diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java index ec41e0c8a..d08ab0aa6 100644 --- a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java +++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglGL1Renderer.java @@ -738,7 +738,6 @@ public class LwjglGL1Renderer implements GL1Renderer { // Resize texture to Power-of-2 size MipMapGenerator.resizeToPowerOf2(img); - } } } @@ -777,7 +776,7 @@ public class LwjglGL1Renderer implements GL1Renderer { TextureUtil.uploadTexture(img, target, i, 0, tdc); } } else {*/ - TextureUtil.uploadTexture(img, target, 0, 0, false); + TextureUtil.uploadTexture(img, target, 0, 0); //} img.clearUpdateNeeded(); @@ -1052,15 +1051,12 @@ public class LwjglGL1Renderer implements GL1Renderer { updateBufferData(interleavedData); } - IntMap buffers = mesh.getBuffers(); if (mesh.getNumLodLevels() > 0) { indices = mesh.getLodLevel(lod); } else { - indices = buffers.get(Type.Index.ordinal()); + indices = mesh.getBuffer(Type.Index); } - for (Entry entry : buffers) { - VertexBuffer vb = entry.getValue(); - + for (VertexBuffer vb : mesh.getBufferList().getArray()) { if (vb.getBufferType() == Type.InterleavedData || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers || vb.getBufferType() == Type.Index) { @@ -1108,9 +1104,8 @@ public class LwjglGL1Renderer implements GL1Renderer { } if (mesh.getNumLodLevels() == 0) { - IntMap bufs = mesh.getBuffers(); - for (Entry entry : bufs) { - if (entry.getValue().getUsage() != VertexBuffer.Usage.Static) { + for (VertexBuffer vb : mesh.getBufferList().getArray()) { + if (vb.getUsage() != VertexBuffer.Usage.Static) { dynamic = true; break; } diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java index d117b72d3..b4a26c353 100644 --- a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -53,12 +53,8 @@ import com.jme3.texture.FrameBuffer.RenderBuffer; import com.jme3.texture.Image; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapAxis; -import com.jme3.util.BufferUtils; -import com.jme3.util.IntMap; import com.jme3.util.IntMap.Entry; -import com.jme3.util.ListMap; -import com.jme3.util.NativeObjectManager; -import com.jme3.util.SafeArrayList; +import com.jme3.util.*; import java.nio.*; import java.util.EnumSet; import java.util.List; @@ -109,7 +105,6 @@ public class LwjglRenderer implements Renderer { private int maxTriCount; private int maxColorTexSamples; private int maxDepthTexSamples; - private boolean tdc; private FrameBuffer lastFb = null; private FrameBuffer mainFbOverride = null; private final Statistics statistics = new Statistics(); @@ -272,6 +267,10 @@ public class LwjglRenderer implements Renderer { if (ctxCaps.GL_ARB_depth_buffer_float) { caps.add(Caps.FloatDepthBuffer); } + + if (ctxCaps.OpenGL30){ + caps.add(Caps.PackedDepthStencilBuffer); + } if (ctxCaps.GL_ARB_draw_instanced) { caps.add(Caps.MeshInstancing); @@ -304,12 +303,8 @@ public class LwjglRenderer implements Renderer { } boolean latc = ctxCaps.GL_EXT_texture_compression_latc; - boolean atdc = ctxCaps.GL_ATI_texture_compression_3dc; - if (latc || atdc) { + if (latc) { caps.add(Caps.TextureCompressionLATC); - if (atdc && !latc) { - tdc = true; - } } if (ctxCaps.GL_EXT_packed_float) { @@ -454,10 +449,12 @@ public class LwjglRenderer implements Renderer { } public void setAlphaToCoverage(boolean value) { - if (value) { - glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); - } else { - glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + if (caps.contains(Caps.Multisample)){ + if (value) { + glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + } else { + glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); + } } } @@ -1270,7 +1267,7 @@ public class LwjglRenderer implements Renderer { switch (type) { case GL_NONE: System.out.println("Type: None"); - return; // note: return from method as other queries will be invalid + break; case GL_TEXTURE: System.out.println("Type: Texture"); break; @@ -1364,8 +1361,8 @@ public class LwjglRenderer implements Renderer { + ":" + fb.getHeight() + " is not supported."); } - TextureUtil.checkFormatSupported(rb.getFormat()); - + TextureUtil.GLImageFormat glFmt = TextureUtil.getImageFormatWithError(rb.getFormat()); + if (fb.getSamples() > 1 && GLContext.getCapabilities().GL_EXT_framebuffer_multisample) { int samples = fb.getSamples(); if (maxFBOSamples < samples) { @@ -1373,12 +1370,12 @@ public class LwjglRenderer implements Renderer { } glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, - TextureUtil.convertTextureFormat(rb.getFormat()), + glFmt.internalFormat, fb.getWidth(), fb.getHeight()); } else { glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, - TextureUtil.convertTextureFormat(rb.getFormat()), + glFmt.internalFormat, fb.getWidth(), fb.getHeight()); } @@ -1854,19 +1851,19 @@ public class LwjglRenderer implements Renderer { return; } for (int i = 0; i < 6; i++) { - TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0, tdc); + TextureUtil.uploadTexture(img, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, 0); } } else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT) { List data = img.getData(); // -1 index specifies prepare data for 2D Array - TextureUtil.uploadTexture(img, target, -1, 0, tdc); + TextureUtil.uploadTexture(img, target, -1, 0); for (int i = 0; i < data.size(); i++) { // upload each slice of 2D array in turn // this time with the appropriate index - TextureUtil.uploadTexture(img, target, i, 0, tdc); + TextureUtil.uploadTexture(img, target, i, 0); } } else { - TextureUtil.uploadTexture(img, target, 0, 0, tdc); + TextureUtil.uploadTexture(img, target, 0, 0); } if (img.getMultiSamples() != imageSamples) { @@ -1978,8 +1975,8 @@ public class LwjglRenderer implements Renderer { return GL_INT; case UnsignedInt: return GL_UNSIGNED_INT; - case Half: - return NVHalfFloat.GL_HALF_FLOAT_NV; +// case Half: +// return NVHalfFloat.GL_HALF_FLOAT_NV; // return ARBHalfFloatVertex.GL_HALF_FLOAT; case Float: return GL_FLOAT; @@ -2343,10 +2340,7 @@ public class LwjglRenderer implements Renderer { updateBufferData(interleavedData); } - IntMap buffers = mesh.getBuffers(); - for (Entry entry : buffers) { - VertexBuffer vb = entry.getValue(); - + for (VertexBuffer vb : mesh.getBufferList().getArray()) { if (vb.getBufferType() == Type.InterleavedData || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers || vb.getBufferType() == Type.Index) { @@ -2376,7 +2370,7 @@ public class LwjglRenderer implements Renderer { } // IntMap buffers = mesh.getBuffers(); - VertexBuffer indices = null; + VertexBuffer indices; if (mesh.getNumLodLevels() > 0) { indices = mesh.getLodLevel(lod); } else { diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java index 2edeea4bb..d2dfc5975 100644 --- a/engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java +++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/TextureUtil.java @@ -36,369 +36,192 @@ import com.jme3.renderer.RendererException; import com.jme3.texture.Image; import com.jme3.texture.Image.Format; import java.nio.ByteBuffer; -import static org.lwjgl.opengl.ATITextureCompression3DC.GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI; -import static org.lwjgl.opengl.EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; -import static org.lwjgl.opengl.EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_LATC1_EXT; -import static org.lwjgl.opengl.EXTTextureCompressionS3TC.*; -import static org.lwjgl.opengl.GL11.*; -import static org.lwjgl.opengl.GL12.*; -import static org.lwjgl.opengl.GL13.glCompressedTexImage2D; -import static org.lwjgl.opengl.GL13.glCompressedTexImage3D; -import static org.lwjgl.opengl.GL14.*; import org.lwjgl.opengl.*; -public class TextureUtil { +class TextureUtil { - private static boolean isFormatSupported(Format fmt, ContextCapabilities caps){ + 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, GL11.GL_ALPHA8, GL11.GL_ALPHA, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Alpha16, GL11.GL_ALPHA16, GL11.GL_ALPHA, GL11.GL_UNSIGNED_BYTE, false); + + // Luminance formats + setFormat(Format.Luminance8, GL11.GL_LUMINANCE8, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Luminance16, GL11.GL_LUMINANCE16, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Luminance16F, ARBTextureFloat.GL_LUMINANCE16F_ARB, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Luminance32F, ARBTextureFloat.GL_LUMINANCE32F_ARB, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, false); + + // Luminance alpha formats + setFormat(Format.Luminance8Alpha8, GL11.GL_LUMINANCE8_ALPHA8, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Luminance16Alpha16, GL11.GL_LUMINANCE16_ALPHA16, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Luminance16FAlpha16F, ARBTextureFloat.GL_LUMINANCE_ALPHA16F_ARB, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, false); + + // Depth formats + setFormat(Format.Depth, GL11.GL_DEPTH_COMPONENT, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Depth16, GL14.GL_DEPTH_COMPONENT16, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Depth24, GL14.GL_DEPTH_COMPONENT24, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Depth32, GL14.GL_DEPTH_COMPONENT32, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.Depth32F, GL30.GL_DEPTH_COMPONENT32F, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, false); + + // Depth stencil formats + setFormat(Format.Depth24Stencil8, GL30.GL_DEPTH24_STENCIL8, GL11.GL_DEPTH_COMPONENT, GL30.GL_UNSIGNED_INT_24_8, false); + + // RGB formats + setFormat(Format.BGR8, GL11.GL_RGB8, EXTBgra.GL_BGR_EXT, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.RGB8, GL11.GL_RGB8, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.RGB10, GL11.GL_RGB10, GL11.GL_RGB, GL12.GL_UNSIGNED_INT_10_10_10_2, false); + setFormat(Format.RGB16, GL11.GL_RGB16, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, false); // might be incorrect + setFormat(Format.RGB16F, ARBTextureFloat.GL_RGB16F_ARB, GL11.GL_RGB, ARBHalfFloatPixel.GL_HALF_FLOAT_ARB, false); + setFormat(Format.RGB32F, ARBTextureFloat.GL_RGB32F_ARB, GL11.GL_RGB, GL11.GL_FLOAT, false); + + // Special RGB formats + setFormat(Format.RGB111110F, EXTPackedFloat.GL_R11F_G11F_B10F_EXT, GL11.GL_RGB, EXTPackedFloat.GL_UNSIGNED_INT_10F_11F_11F_REV_EXT, false); + setFormat(Format.RGB9E5, EXTTextureSharedExponent.GL_RGB9_E5_EXT, GL11.GL_RGB, EXTTextureSharedExponent.GL_UNSIGNED_INT_5_9_9_9_REV_EXT, false); + setFormat(Format.RGB16F_to_RGB111110F, EXTPackedFloat.GL_R11F_G11F_B10F_EXT, GL11.GL_RGB, ARBHalfFloatPixel.GL_HALF_FLOAT_ARB, false); + setFormat(Format.RGB16F_to_RGB111110F, EXTTextureSharedExponent.GL_RGB9_E5_EXT, GL11.GL_RGB, ARBHalfFloatPixel.GL_HALF_FLOAT_ARB, false); + + // RGBA formats + setFormat(Format.ABGR8, GL11.GL_RGBA8, EXTAbgr.GL_ABGR_EXT, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.RGB5A1, GL11.GL_RGB5_A1, GL11.GL_RGBA, GL12.GL_UNSIGNED_SHORT_5_5_5_1, false); + setFormat(Format.RGBA8, GL11.GL_RGBA8, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, false); + setFormat(Format.RGBA16, GL11.GL_RGBA16, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, false); // might be incorrect + setFormat(Format.RGBA16F, ARBTextureFloat.GL_RGBA16F_ARB, GL11.GL_RGBA, ARBHalfFloatPixel.GL_HALF_FLOAT_ARB, false); + setFormat(Format.RGBA32F, ARBTextureFloat.GL_RGBA32F_ARB, GL11.GL_RGBA, GL11.GL_FLOAT, false); + + // DXT formats + setFormat(Format.DXT1, EXTTextureCompressionS3TC.GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, true); + setFormat(Format.DXT1A, EXTTextureCompressionS3TC.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, true); + setFormat(Format.DXT3, EXTTextureCompressionS3TC.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, true); + setFormat(Format.DXT5, EXTTextureCompressionS3TC.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, true); + + // LTC/LATC/3Dc formats + setFormat(Format.LTC, EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_LATC1_EXT, GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, true); + setFormat(Format.LATC, EXTTextureCompressionLATC.GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT, GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, true); + } + + public static GLImageFormat getImageFormat(ContextCapabilities caps, Format fmt){ switch (fmt){ - case ARGB4444: - return false; + case ABGR8: + if (!caps.GL_EXT_abgr){ + return null; + } + break; case BGR8: - return caps.OpenGL12 || caps.GL_EXT_bgra; + if (!caps.OpenGL12 && !caps.GL_EXT_bgra){ + return null; + } + break; case DXT1: case DXT1A: case DXT3: case DXT5: - return caps.GL_EXT_texture_compression_s3tc; + if (!caps.GL_EXT_texture_compression_s3tc) { + return null; + } + break; case Depth: case Depth16: case Depth24: case Depth32: - return caps.OpenGL14 || caps.GL_ARB_depth_texture; - case Depth32F: + if (!caps.OpenGL14 && !caps.GL_ARB_depth_texture){ + return null; + } + break; case Luminance16F: case Luminance16FAlpha16F: case Luminance32F: + if (!caps.GL_ARB_texture_float){ + return null; + } + break; + case RGB16F: + case RGB32F: case RGBA16F: case RGBA32F: - return caps.OpenGL30 || caps.GL_ARB_texture_float; + if (!caps.OpenGL30 && !caps.GL_ARB_texture_float){ + return null; + } + break; + case Depth32F: + if (!caps.OpenGL30 && !caps.GL_NV_depth_buffer_float){ + return null; + } + break; case LATC: case LTC: - return caps.GL_EXT_texture_compression_latc; + if (!caps.GL_EXT_texture_compression_latc){ + return null; + } + break; case RGB9E5: case RGB16F_to_RGB9E5: - return caps.OpenGL30 || caps.GL_EXT_texture_shared_exponent; + if (!caps.OpenGL30 && !caps.GL_EXT_texture_shared_exponent){ + return null; + } + break; case RGB111110F: case RGB16F_to_RGB111110F: - return caps.OpenGL30 || caps.GL_EXT_packed_float; - default: - return true; + if (!caps.OpenGL30 && !caps.GL_EXT_packed_float){ + return null; + } + break; } + return formatToGL[fmt.ordinal()]; } - - public static void checkFormatSupported(Format fmt) { - if (!isFormatSupported(fmt, GLContext.getCapabilities())) { + + public static GLImageFormat getImageFormatWithError(Format fmt) { + GLImageFormat glFmt = getImageFormat(GLContext.getCapabilities(), fmt); + if (glFmt == null) { throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); } + return glFmt; } - - public static int convertTextureFormat(Format fmt){ - switch (fmt){ - case Alpha16: - return GL_ALPHA16; - case Alpha8: - return GL_ALPHA8; - case DXT1: - return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - case DXT1A: - return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - case DXT3: - return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case DXT5: - return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case LATC: - return GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; - case Depth: - return GL_DEPTH_COMPONENT; - case Depth16: - return GL_DEPTH_COMPONENT16; - case Depth24: - return GL_DEPTH_COMPONENT24; - case Depth32: - return GL_DEPTH_COMPONENT32; - case Depth32F: - return ARBDepthBufferFloat.GL_DEPTH_COMPONENT32F; - case Luminance8Alpha8: - return GL_LUMINANCE8_ALPHA8; - case Luminance16Alpha16: - return GL_LUMINANCE16_ALPHA16; - case Luminance16FAlpha16F: - return ARBTextureFloat.GL_LUMINANCE_ALPHA16F_ARB; - case Intensity8: - return GL_INTENSITY8; - case Intensity16: - return GL_INTENSITY16; - case Luminance8: - return GL_LUMINANCE8; - case Luminance16: - return GL_LUMINANCE16; - case Luminance16F: - return ARBTextureFloat.GL_LUMINANCE16F_ARB; - case Luminance32F: - return ARBTextureFloat.GL_LUMINANCE32F_ARB; - case RGB10: - return GL_RGB10; - case RGB16: - return GL_RGB16; - case RGB111110F: - return EXTPackedFloat.GL_R11F_G11F_B10F_EXT; - case RGB9E5: - return EXTTextureSharedExponent.GL_RGB9_E5_EXT; - case RGB16F: - return ARBTextureFloat.GL_RGB16F_ARB; - case RGBA16F: - return ARBTextureFloat.GL_RGBA16F_ARB; - case RGB32F: - return ARBTextureFloat.GL_RGB32F_ARB; - case RGB5A1: - return GL_RGB5_A1; - case BGR8: - return GL_RGB8; - case RGB8: - return GL_RGB8; - case RGBA16: - return GL_RGBA16; - case RGBA8: - return GL_RGBA8; - default: - throw new UnsupportedOperationException("Unrecognized format: "+fmt); - } - } - - public static void uploadTexture(Image img, + + public static void uploadTexture(Image image, int target, int index, - int border, - boolean tdc){ - Image.Format fmt = img.getFormat(); - - checkFormatSupported(fmt); + int border){ + + Image.Format fmt = image.getFormat(); + GLImageFormat glFmt = getImageFormatWithError(fmt); ByteBuffer data; - if (index >= 0 && img.getData() != null && img.getData().size() > 0){ - data = img.getData(index); + if (index >= 0 && image.getData() != null && image.getData().size() > 0){ + data = image.getData(index); }else{ data = null; } - int width = img.getWidth(); - int height = img.getHeight(); - int depth = img.getDepth(); + int width = image.getWidth(); + int height = image.getHeight(); + int depth = image.getDepth(); - boolean compress = false; - int internalFormat = -1; - int format = -1; - int dataType = -1; - - switch (fmt){ - case Alpha16: - internalFormat = GL_ALPHA16; - format = GL_ALPHA; - dataType = GL_UNSIGNED_BYTE; - break; - case Alpha8: - internalFormat = GL_ALPHA8; - format = GL_ALPHA; - dataType = GL_UNSIGNED_BYTE; - break; - case DXT1: - compress = true; - internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - format = GL_RGB; - dataType = GL_UNSIGNED_BYTE; - break; - case DXT1A: - compress = true; - internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - format = GL_RGBA; - dataType = GL_UNSIGNED_BYTE; - break; - case DXT3: - compress = true; - internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - format = GL_RGBA; - dataType = GL_UNSIGNED_BYTE; - break; - case DXT5: - compress = true; - internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - format = GL_RGBA; - dataType = GL_UNSIGNED_BYTE; - break; - case LATC: - compress = true; - if (tdc){ - internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_3DC_ATI; - }else{ - internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; - } - format = GL_LUMINANCE_ALPHA; - dataType = GL_UNSIGNED_BYTE; - break; - case LTC: - compress = true; - internalFormat = GL_COMPRESSED_LUMINANCE_LATC1_EXT; - format = GL_LUMINANCE_ALPHA; - dataType = GL_UNSIGNED_BYTE; - break; - case Depth: - internalFormat = GL_DEPTH_COMPONENT; - format = GL_DEPTH_COMPONENT; - dataType = GL_UNSIGNED_BYTE; - break; - case Depth16: - internalFormat = GL_DEPTH_COMPONENT16; - format = GL_DEPTH_COMPONENT; - dataType = GL_UNSIGNED_BYTE; - break; - case Depth24: - internalFormat = GL_DEPTH_COMPONENT24; - format = GL_DEPTH_COMPONENT; - dataType = GL_UNSIGNED_BYTE; - break; - case Depth32: - internalFormat = GL_DEPTH_COMPONENT32; - format = GL_DEPTH_COMPONENT; - dataType = GL_UNSIGNED_BYTE; - break; - case Depth32F: - internalFormat = NVDepthBufferFloat.GL_DEPTH_COMPONENT32F_NV; - format = GL_DEPTH_COMPONENT; - dataType = GL_FLOAT; - break; - case Luminance16FAlpha16F: - internalFormat = ARBTextureFloat.GL_LUMINANCE_ALPHA16F_ARB; - format = GL_LUMINANCE_ALPHA; - dataType = GL_UNSIGNED_BYTE; - break; - case Intensity8: - internalFormat = GL_INTENSITY8; - format = GL_INTENSITY; - dataType = GL_UNSIGNED_BYTE; - break; - case Intensity16: - internalFormat = GL_INTENSITY16; - format = GL_INTENSITY; - dataType = GL_UNSIGNED_BYTE; - break; - case Luminance8: - internalFormat = GL_LUMINANCE8; - format = GL_LUMINANCE; - dataType = GL_UNSIGNED_BYTE; - break; - case Luminance8Alpha8: - internalFormat = GL_LUMINANCE8_ALPHA8; - format = GL_LUMINANCE_ALPHA; - dataType = GL_UNSIGNED_BYTE; - break; - case Luminance16Alpha16: - internalFormat = GL_LUMINANCE16_ALPHA16; - format = GL_LUMINANCE_ALPHA; - dataType = GL_UNSIGNED_BYTE; - break; - case Luminance16: - internalFormat = GL_LUMINANCE16; - format = GL_LUMINANCE; - dataType = GL_UNSIGNED_BYTE; - break; - case Luminance16F: - internalFormat = ARBTextureFloat.GL_LUMINANCE16F_ARB; - format = GL_LUMINANCE; - dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB; - break; - case Luminance32F: - internalFormat = ARBTextureFloat.GL_LUMINANCE32F_ARB; - format = GL_LUMINANCE; - dataType = GL_FLOAT; - break; - case RGB10: - internalFormat = GL_RGB10; - format = GL_RGB; - dataType = GL_UNSIGNED_BYTE; - break; - case RGB16: - internalFormat = GL_RGB16; - format = GL_RGB; - dataType = GL_UNSIGNED_BYTE; - break; - case RGB111110F: - internalFormat = EXTPackedFloat.GL_R11F_G11F_B10F_EXT; - format = GL_RGB; - dataType = EXTPackedFloat.GL_UNSIGNED_INT_10F_11F_11F_REV_EXT; - break; - case RGB16F_to_RGB111110F: - internalFormat = EXTPackedFloat.GL_R11F_G11F_B10F_EXT; - format = GL_RGB; - dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB; - break; - case RGB16F_to_RGB9E5: - internalFormat = EXTTextureSharedExponent.GL_RGB9_E5_EXT; - format = GL_RGB; - dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB; - break; - case RGB9E5: - internalFormat = EXTTextureSharedExponent.GL_RGB9_E5_EXT; - format = GL_RGB; - dataType = EXTTextureSharedExponent.GL_UNSIGNED_INT_5_9_9_9_REV_EXT; - break; - case RGB16F: - internalFormat = ARBTextureFloat.GL_RGB16F_ARB; - format = GL_RGB; - dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB; - break; - case RGBA16F: - internalFormat = ARBTextureFloat.GL_RGBA16F_ARB; - format = GL_RGBA; - dataType = ARBHalfFloatPixel.GL_HALF_FLOAT_ARB; - break; - case RGB32F: - internalFormat = ARBTextureFloat.GL_RGB32F_ARB; - format = GL_RGB; - dataType = GL_FLOAT; - break; - case RGBA32F: - internalFormat = ARBTextureFloat.GL_RGBA32F_ARB; - format = GL_RGBA; - dataType = GL_FLOAT; - break; - case RGB5A1: - internalFormat = GL_RGB5_A1; - format = GL_RGBA; - dataType = GL_UNSIGNED_BYTE; - break; - case RGB8: - internalFormat = GL_RGB8; - format = GL_RGB; - dataType = GL_UNSIGNED_BYTE; - break; - case BGR8: - internalFormat = GL_RGB8; - format = GL_BGR; - dataType = GL_UNSIGNED_BYTE; - break; - case RGBA16: - internalFormat = GL_RGBA16; - format = GL_RGBA; - dataType = GL_UNSIGNED_BYTE; - break; - case RGBA8: - internalFormat = GL_RGBA8; - format = GL_RGBA; - dataType = GL_UNSIGNED_BYTE; - break; - case ABGR8: - internalFormat = GL_RGBA8; - format = EXTAbgr.GL_ABGR_EXT; - dataType = GL_UNSIGNED_BYTE; - break; - default: - throw new UnsupportedOperationException("Unrecognized format: "+fmt); + if (data != null) { + GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); } - if (data != null) - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - int[] mipSizes = img.getMipMapSizes(); + int[] mipSizes = image.getMipMapSizes(); int pos = 0; // TODO: Remove unneccessary allocation if (mipSizes == null){ @@ -409,7 +232,7 @@ public class TextureUtil { } boolean subtex = false; - int samples = img.getMultiSamples(); + int samples = image.getMultiSamples(); for (int i = 0; i < mipSizes.length; i++){ int mipWidth = Math.max(1, width >> i); @@ -421,95 +244,96 @@ public class TextureUtil { data.limit(pos + mipSizes[i]); } - if (compress && data != null){ - if (target == GL_TEXTURE_3D){ - glCompressedTexImage3D(target, - i, - internalFormat, - mipWidth, - mipHeight, - mipDepth, - border, - data); + if (glFmt.compressed && data != null){ + if (target == GL12.GL_TEXTURE_3D){ + GL13.glCompressedTexImage3D(target, + i, + glFmt.internalFormat, + mipWidth, + mipHeight, + mipDepth, + border, + data); }else{ //all other targets use 2D: array, cubemap, 2d - glCompressedTexImage2D(target, - i, - internalFormat, - mipWidth, - mipHeight, - border, - data); + GL13.glCompressedTexImage2D(target, + i, + glFmt.internalFormat, + mipWidth, + mipHeight, + border, + data); } }else{ - if (target == GL_TEXTURE_3D){ - glTexImage3D(target, - i, - internalFormat, - mipWidth, - mipHeight, - mipDepth, - border, - format, - dataType, - data); + if (target == GL12.GL_TEXTURE_3D){ + GL12.glTexImage3D(target, + i, + glFmt.internalFormat, + mipWidth, + mipHeight, + mipDepth, + border, + glFmt.format, + glFmt.dataType, + data); }else if (target == EXTTextureArray.GL_TEXTURE_2D_ARRAY_EXT){ // prepare data for 2D array // or upload slice if (index == -1){ - glTexImage3D(target, - 0, - internalFormat, - mipWidth, - mipHeight, - img.getData().size(), //# of slices - border, - format, - dataType, - data); + GL12.glTexImage3D(target, + 0, + glFmt.internalFormat, + mipWidth, + mipHeight, + image.getData().size(), //# of slices + border, + glFmt.format, + glFmt.dataType, + data); }else{ - glTexSubImage3D(target, - i, // level - 0, // xoffset - 0, // yoffset - index, // zoffset - width, // width - height, // height - 1, // depth - format, - dataType, - data); + GL12.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) + if (samples > 1){ throw new IllegalStateException("Cannot update multisample textures"); + } - glTexSubImage2D(target, - i, - 0, 0, - mipWidth, mipHeight, - format, - dataType, - data); + GL11.glTexSubImage2D(target, + i, + 0, 0, + mipWidth, mipHeight, + glFmt.format, + glFmt.dataType, + data); }else{ if (samples > 1){ ARBTextureMultisample.glTexImage2DMultisample(target, samples, - internalFormat, + glFmt.internalFormat, mipWidth, mipHeight, true); }else{ - glTexImage2D(target, - i, - internalFormat, - mipWidth, - mipHeight, - border, - format, - dataType, - data); + GL11.glTexImage2D(target, + i, + glFmt.internalFormat, + mipWidth, + mipHeight, + border, + glFmt.format, + glFmt.dataType, + data); } } }