com.jme3.renderer.opengl.TextureUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-core Show documentation
Show all versions of jme3-core Show documentation
jMonkeyEngine is a 3-D game engine for adventurous Java developers
/*
* Copyright (c) 2009-2021 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.opengl;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RendererException;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ColorSpace;
import java.nio.ByteBuffer;
import java.util.EnumSet;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Internal utility class used by {@link GLRenderer} to manage textures.
*
* @author Kirill Vainer
*/
final class TextureUtil {
private static final Logger logger = Logger.getLogger(TextureUtil.class.getName());
private final GL gl;
private final GL2 gl2;
private final GLExt glext;
private GLImageFormat[][] formats;
private boolean supportUnpackRowLength;
public TextureUtil(GL gl, GL2 gl2, GLExt glext) {
this.gl = gl;
this.gl2 = gl2;
this.glext = glext;
}
public void initialize(EnumSet caps) {
supportUnpackRowLength = caps.contains(Caps.UnpackRowLength);
this.formats = GLImageFormats.getFormatsForCaps(caps);
if (logger.isLoggable(Level.FINE)) {
StringBuilder sb = new StringBuilder();
sb.append("Supported texture formats: \n");
for (int i = 0; i < Format.values().length; i++) {
Format format = Format.values()[i];
if (formats[0][i] != null) {
boolean srgb = formats[1][i] != null;
sb.append("\t").append(format.toString());
sb.append(" (Linear");
if (srgb) sb.append("/sRGB");
sb.append(")\n");
}
}
logger.log(Level.FINE, sb.toString());
}
}
public GLImageFormat getImageFormat(Format fmt, boolean isSrgb) {
if (isSrgb) {
return formats[1][fmt.ordinal()];
} else {
return formats[0][fmt.ordinal()];
}
}
public GLImageFormat getImageFormatWithError(Format fmt, boolean isSrgb) {
//if the passed format is one kind of depth there is no point in getting the srgb format;
isSrgb = isSrgb && !fmt.isDepthFormat();
GLImageFormat glFmt = getImageFormat(fmt, isSrgb);
if (glFmt == null && isSrgb) {
glFmt = getImageFormat(fmt, false);
logger.log(Level.WARNING, "No sRGB format available for ''{0}''. Falling back to linear.", fmt);
}
if (glFmt == null) {
throw new RendererException("Image format '" + fmt + "' is unsupported by the video hardware.");
}
return glFmt;
}
private void setupTextureSwizzle(int target, Format format) {
// Needed for OpenGL 3.3 to support luminance / alpha formats
switch (format) {
case Alpha8:
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_ZERO);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_ZERO);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_ZERO);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_RED);
break;
case Luminance8:
case Luminance16F:
case Luminance32F:
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_ONE);
break;
case Luminance8Alpha8:
case Luminance16FAlpha16F:
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_RED);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_GREEN);
break;
case ABGR8:
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_R, GL.GL_ALPHA);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_G, GL.GL_BLUE);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_B, GL.GL_GREEN);
gl.glTexParameteri(target, GL3.GL_TEXTURE_SWIZZLE_A, GL.GL_RED);
break;
default:
throw new UnsupportedOperationException();
}
}
private void uploadTextureLevel(GLImageFormat format, int target, int level, int slice, int sliceCount, int width, int height, int depth, int samples, ByteBuffer data) {
if (format.compressed && data != null) {
if (target == GL2.GL_TEXTURE_3D) {
// For 3D textures, we upload the entire mipmap level.
gl2.glCompressedTexImage3D(target,
level,
format.internalFormat,
width,
height,
depth,
0,
data);
} else if (target == GLExt.GL_TEXTURE_2D_ARRAY_EXT) {
// For texture arrays, only upload 1 slice at a time.
// zoffset specifies slice index, and depth is 1 to indicate
// a single texture in the array.
gl2.glCompressedTexSubImage3D(target,
level,
0,
0,
slice,
width,
height,
1,
format.internalFormat,
data);
} else {
// Cubemaps also use 2D upload.
gl2.glCompressedTexImage2D(target,
level,
format.internalFormat,
width,
height,
0,
data);
}
} else {
// (Non-compressed OR allocating texture storage for FBO)
if (target == GL2.GL_TEXTURE_3D) {
gl2.glTexImage3D(target,
level,
format.internalFormat,
width,
height,
depth,
0,
format.format,
format.dataType,
data);
} else if (target == GLExt.GL_TEXTURE_2D_ARRAY_EXT) {
if (slice == -1) {
// Allocate texture storage (data is NULL)
gl2.glTexImage3D(target,
level,
format.internalFormat,
width,
height,
sliceCount, //# of slices
0,
format.format,
format.dataType,
data);
} else {
// For texture arrays, only upload 1 slice at a time.
// zoffset specifies slice index, and depth is 1 to indicate
// a single texture in the array.
gl2.glTexSubImage3D(target,
level, // level
0, // xoffset
0, // yoffset
slice, // zoffset
width, // width
height, // height
1, // depth
format.format,
format.dataType,
data);
}
} else {
// 2D multisampled image.
if (samples > 1) {
glext.glTexImage2DMultisample(target,
samples,
format.internalFormat,
width,
height,
true);
} else {
// Regular 2D image
gl.glTexImage2D(target,
level,
format.internalFormat,
width,
height,
0,
format.format,
format.dataType,
data);
}
}
}
}
public void uploadTexture(Image image,
int target,
int index,
boolean linearizeSrgb) {
boolean getSrgbFormat = image.getColorSpace() == ColorSpace.sRGB && linearizeSrgb;
Image.Format jmeFormat = image.getFormat();
GLImageFormat oglFormat = getImageFormatWithError(jmeFormat, getSrgbFormat);
ByteBuffer data = null;
int sliceCount = 1;
if (index >= 0) {
data = image.getData(index);
}
if (image.getData() != null && image.getData().size() > 0) {
sliceCount = image.getData().size();
}
int width = image.getWidth();
int height = image.getHeight();
int depth = image.getDepth();
int[] mipSizes = image.getMipMapSizes();
int pos = 0;
// TODO: Remove unnecessary allocation
if (mipSizes == null) {
if (data != null) {
mipSizes = new int[]{data.capacity()};
} else {
mipSizes = new int[]{width * height * jmeFormat.getBitsPerPixel() / 8};
}
}
int samples = image.getMultiSamples();
// For OGL3 core: setup texture swizzle.
if (oglFormat.swizzleRequired) {
setupTextureSwizzle(target, jmeFormat);
}
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]);
}
uploadTextureLevel(oglFormat, target, i, index, sliceCount, mipWidth, mipHeight, mipDepth, samples, data);
pos += mipSizes[i];
}
}
/**
* @deprecated Use uploadSubTexture(int target, Image src, int index,int targetX, int targetY,int srcX,int srcY, int areaWidth,int areaHeight, boolean linearizeSrgb)
*/
@Deprecated
public void uploadSubTexture(Image image, int target, int index, int x, int y, boolean linearizeSrgb) {
if (target != GL.GL_TEXTURE_2D || image.getDepth() > 1) {
throw new UnsupportedOperationException("Updating non-2D texture is not supported");
}
if (image.getMipMapSizes() != null) {
throw new UnsupportedOperationException("Updating mip-mapped images is not supported");
}
if (image.getMultiSamples() > 1) {
throw new UnsupportedOperationException("Updating multisampled images is not supported");
}
Image.Format jmeFormat = image.getFormat();
if (jmeFormat.isCompressed()) {
throw new UnsupportedOperationException("Updating compressed images is not supported");
} else if (jmeFormat.isDepthFormat()) {
throw new UnsupportedOperationException("Updating depth images is not supported");
}
boolean getSrgbFormat = image.getColorSpace() == ColorSpace.sRGB && linearizeSrgb;
GLImageFormat oglFormat = getImageFormatWithError(jmeFormat, getSrgbFormat);
ByteBuffer data = null;
if (index >= 0) {
data = image.getData(index);
}
if (data == null) {
throw new IndexOutOfBoundsException("The image index " + index + " is not valid for the given image");
}
data.position(0);
data.limit(data.capacity());
gl.glTexSubImage2D(target, 0, x, y, image.getWidth(), image.getHeight(),
oglFormat.format, oglFormat.dataType, data);
}
public void uploadSubTexture(int target, Image src, int index, int targetX, int targetY, int areaX, int areaY, int areaWidth, int areaHeight, boolean linearizeSrgb) {
if (target != GL.GL_TEXTURE_2D || src.getDepth() > 1) {
throw new UnsupportedOperationException("Updating non-2D texture is not supported");
}
if (src.getMipMapSizes() != null) {
throw new UnsupportedOperationException("Updating mip-mapped images is not supported");
}
if (src.getMultiSamples() > 1) {
throw new UnsupportedOperationException("Updating multisampled images is not supported");
}
Image.Format jmeFormat = src.getFormat();
if (jmeFormat.isCompressed()) {
throw new UnsupportedOperationException("Updating compressed images is not supported");
} else if (jmeFormat.isDepthFormat()) {
throw new UnsupportedOperationException("Updating depth images is not supported");
}
boolean getSrgbFormat = src.getColorSpace() == ColorSpace.sRGB && linearizeSrgb;
GLImageFormat oglFormat = getImageFormatWithError(jmeFormat, getSrgbFormat);
ByteBuffer data = src.getData(index);
if (data == null) {
throw new IndexOutOfBoundsException("The image index " + index + " is not valid for the given image");
}
int Bpp = src.getFormat().getBitsPerPixel() / 8;
int srcWidth = src.getWidth();
int cpos = data.position();
int skip = areaX;
skip += areaY * srcWidth;
skip *= Bpp;
data.position(skip);
boolean needsStride = srcWidth != areaWidth;
if (needsStride && (!supportUnpackRowLength)) { // doesn't support stride, copy row by row (slower).
for (int i = 0; i < areaHeight; i++) {
data.position(skip + (srcWidth * Bpp * i));
gl.glTexSubImage2D(target, 0, targetX, targetY + i, areaWidth, 1, oglFormat.format, oglFormat.dataType, data);
}
} else {
if (needsStride)
gl2.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, srcWidth);
gl.glTexSubImage2D(target, 0, targetX, targetY, areaWidth, areaHeight, oglFormat.format, oglFormat.dataType, data);
if (needsStride)
gl2.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, 0);
}
data.position(cpos);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy