| /* |
| * Copyright (c) 2009-2010 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.lwjgl; |
| |
| 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 { |
| |
| private static boolean isFormatSupported(Format fmt, ContextCapabilities caps){ |
| switch (fmt){ |
| case ARGB4444: |
| return false; |
| case BGR8: |
| return caps.OpenGL12 || caps.GL_EXT_bgra; |
| case DXT1: |
| case DXT1A: |
| case DXT3: |
| case DXT5: |
| return caps.GL_EXT_texture_compression_s3tc; |
| case Depth: |
| case Depth16: |
| case Depth24: |
| case Depth32: |
| return caps.OpenGL14 || caps.GL_ARB_depth_texture; |
| case Depth32F: |
| case Luminance16F: |
| case Luminance16FAlpha16F: |
| case Luminance32F: |
| case RGBA16F: |
| case RGBA32F: |
| return caps.OpenGL30 || caps.GL_ARB_texture_float; |
| case LATC: |
| case LTC: |
| return caps.GL_EXT_texture_compression_latc; |
| case RGB9E5: |
| case RGB16F_to_RGB9E5: |
| return caps.OpenGL30 || caps.GL_EXT_texture_shared_exponent; |
| case RGB111110F: |
| case RGB16F_to_RGB111110F: |
| return caps.OpenGL30 || caps.GL_EXT_packed_float; |
| default: |
| return true; |
| } |
| } |
| |
| public static void checkFormatSupported(Format fmt) { |
| if (!isFormatSupported(fmt, GLContext.getCapabilities())) { |
| throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware."); |
| } |
| } |
| |
| 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, |
| int target, |
| int index, |
| int border, |
| boolean tdc){ |
| Image.Format fmt = img.getFormat(); |
| |
| checkFormatSupported(fmt); |
| |
| ByteBuffer data; |
| if (index >= 0 && img.getData() != null && img.getData().size() > 0){ |
| data = img.getData(index); |
| }else{ |
| data = null; |
| } |
| |
| int width = img.getWidth(); |
| int height = img.getHeight(); |
| int depth = img.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) |
| glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| |
| int[] mipSizes = img.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 = img.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 (compress && data != null){ |
| if (target == GL_TEXTURE_3D){ |
| glCompressedTexImage3D(target, |
| i, |
| internalFormat, |
| mipWidth, |
| mipHeight, |
| mipDepth, |
| border, |
| data); |
| }else{ |
| //all other targets use 2D: array, cubemap, 2d |
| glCompressedTexImage2D(target, |
| i, |
| internalFormat, |
| mipWidth, |
| mipHeight, |
| border, |
| data); |
| } |
| }else{ |
| if (target == GL_TEXTURE_3D){ |
| glTexImage3D(target, |
| i, |
| internalFormat, |
| mipWidth, |
| mipHeight, |
| mipDepth, |
| border, |
| format, |
| 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); |
| }else{ |
| glTexSubImage3D(target, |
| i, // level |
| 0, // xoffset |
| 0, // yoffset |
| index, // zoffset |
| width, // width |
| height, // height |
| 1, // depth |
| format, |
| dataType, |
| data); |
| } |
| }else{ |
| if (subtex){ |
| if (samples > 1) |
| throw new IllegalStateException("Cannot update multisample textures"); |
| |
| glTexSubImage2D(target, |
| i, |
| 0, 0, |
| mipWidth, mipHeight, |
| format, |
| dataType, |
| data); |
| }else{ |
| if (samples > 1){ |
| ARBTextureMultisample.glTexImage2DMultisample(target, |
| samples, |
| internalFormat, |
| mipWidth, |
| mipHeight, |
| true); |
| }else{ |
| glTexImage2D(target, |
| i, |
| internalFormat, |
| mipWidth, |
| mipHeight, |
| border, |
| format, |
| dataType, |
| data); |
| } |
| } |
| } |
| } |
| |
| pos += mipSizes[i]; |
| } |
| } |
| |
| } |