// bind our texture id to this unit.
doTextureBind(texture, unit, false);
// pass image data to OpenGL
final Image image = texture.getImage();
final boolean hasBorder = texture.hasBorder();
if (image == null) {
logger.warning("Image data for texture is null.");
}
// set alignment to support images with width % 4 != 0, as images are
// not aligned
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
// Get texture image data. Not all textures have image data.
// For example, ApplyMode.Combine modes can use primary colors,
// texture output, and constants to modify fragments via the
// texture units.
if (image != null) {
final int maxSize = caps.getMaxTextureSize();
final int actualWidth = image.getWidth();
final int actualHeight = image.getHeight();
final boolean needsPowerOfTwo = !caps.isNonPowerOfTwoTextureSupported()
&& (!MathUtils.isPowerOfTwo(image.getWidth()) || !MathUtils.isPowerOfTwo(image.getHeight()));
if (actualWidth > maxSize || actualHeight > maxSize || needsPowerOfTwo) {
if (needsPowerOfTwo) {
logger.warning("(card unsupported) Attempted to apply texture with size that is not power of 2: "
+ image.getWidth() + " x " + image.getHeight());
}
if (actualWidth > maxSize || actualHeight > maxSize) {
logger.warning("(card unsupported) Attempted to apply texture with size bigger than max texture size ["
+ maxSize + "]: " + image.getWidth() + " x " + image.getHeight());
}
int w = actualWidth;
if (needsPowerOfTwo) {
w = MathUtils.nearestPowerOfTwo(actualWidth);
}
if (w > maxSize) {
w = maxSize;
}
int h = actualHeight;
if (needsPowerOfTwo) {
h = MathUtils.nearestPowerOfTwo(actualHeight);
}
if (h > maxSize) {
h = maxSize;
}
logger.warning("Rescaling image to " + w + " x " + h + " !!!");
// must rescale image to get "top" mipmap texture image
final int pixFormat = LwjglTextureUtil.getGLPixelFormat(image.getDataFormat());
final int pixDataType = LwjglTextureUtil.getGLPixelDataType(image.getDataType());
final int bpp = ImageUtils.getPixelByteSize(image.getDataFormat(), image.getDataType());
final ByteBuffer scaledImage = BufferUtils.createByteBuffer((w + 4) * h * bpp);
final int error = MipMap.gluScaleImage(pixFormat, actualWidth, actualHeight, pixDataType,
image.getData(0), w, h, pixDataType, scaledImage);
if (error != 0) {
Util.checkGLError();
}
image.setWidth(w);
image.setHeight(h);
image.setData(scaledImage);
}
if (!texture.getMinificationFilter().usesMipMapLevels() && !texture.getTextureStoreFormat().isCompressed()) {
// Load textures which do not need mipmap auto-generating and
// which aren't using compressed images.
switch (texture.getType()) {
case TwoDimensional:
// ensure the buffer is ready for reading
image.getData(0).rewind();
// send top level to card
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getHeight(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0));
break;
case OneDimensional:
// ensure the buffer is ready for reading
image.getData(0).rewind();
// send top level to card
GL11.glTexImage1D(GL11.GL_TEXTURE_1D, 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0));
break;
case ThreeDimensional:
if (caps.isTexture3DSupported()) {
// concat data into single buffer:
int dSize = 0;
int count = 0;
ByteBuffer data = null;
for (int x = 0; x < image.getData().size(); x++) {
if (image.getData(x) != null) {
data = image.getData(x);
dSize += data.limit();
count++;
}
}
// reuse buffer if we can.
if (count != 1) {
data = BufferUtils.createByteBuffer(dSize);
for (int x = 0; x < image.getData().size(); x++) {
if (image.getData(x) != null) {
data.put(image.getData(x));
}
}
// ensure the buffer is ready for reading
data.flip();
}
// send top level to card
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getHeight(), image.getDepth(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data);
} else {
logger.warning("This card does not support Texture3D.");
}
break;
case CubeMap:
// NOTE: Cubemaps MUST be square, so height is ignored
// on purpose.
if (caps.isTextureCubeMapSupported()) {
for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) {
// ensure the buffer is ready for reading
image.getData(face.ordinal()).rewind();
// send top level to card
GL11.glTexImage2D(getGLCubeMapFace(face), 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getWidth(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()),
image.getData(face.ordinal()));
}
} else {
logger.warning("This card does not support Cubemaps.");
}
break;
}
} else if (texture.getMinificationFilter().usesMipMapLevels() && !image.hasMipmaps()
&& !texture.getTextureStoreFormat().isCompressed()) {
// For textures which need mipmaps auto-generating and which
// aren't using compressed images, generate the mipmaps.
// A new mipmap builder may be needed to build mipmaps for
// compressed textures.
if (caps.isAutomaticMipmapsSupported()) {
// Flag the card to generate mipmaps
GL11.glTexParameteri(getGLType(type), SGISGenerateMipmap.GL_GENERATE_MIPMAP_SGIS, GL11.GL_TRUE);
}
switch (type) {
case TwoDimensional:
// ensure the buffer is ready for reading
image.getData(0).rewind();
if (caps.isAutomaticMipmapsSupported()) {
// send top level to card
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getHeight(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0));
} else {
// send to card
GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getHeight(),
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0));
}
break;
case OneDimensional:
// ensure the buffer is ready for reading
image.getData(0).rewind();
if (caps.isAutomaticMipmapsSupported()) {
// send top level to card
GL11.glTexImage1D(GL11.GL_TEXTURE_1D, 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), image.getData(0));
} else {
// Note: LWJGL's GLU class does not support
// gluBuild1DMipmaps.
logger.warning("non-fbo 1d mipmap generation is not currently supported. Use DDS or a non-mipmap minification filter.");
return;
}
break;
case ThreeDimensional:
if (caps.isTexture3DSupported()) {
if (caps.isAutomaticMipmapsSupported()) {
// concat data into single buffer:
int dSize = 0;
int count = 0;
ByteBuffer data = null;
for (int x = 0; x < image.getData().size(); x++) {
if (image.getData(x) != null) {
data = image.getData(x);
dSize += data.limit();
count++;
}
}
// reuse buffer if we can.
if (count != 1) {
data = BufferUtils.createByteBuffer(dSize);
for (int x = 0; x < image.getData().size(); x++) {
if (image.getData(x) != null) {
data.put(image.getData(x));
}
}
// ensure the buffer is ready for reading
data.flip();
}
// send top level to card
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getHeight(), image.getDepth(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data);
} else {
// Note: LWJGL's GLU class does not support
// gluBuild3DMipmaps.
logger.warning("non-fbo 3d mipmap generation is not currently supported. Use DDS or a non-mipmap minification filter.");
return;
}
} else {
logger.warning("This card does not support Texture3D.");
return;
}
break;
case CubeMap:
// NOTE: Cubemaps MUST be square, so height is ignored
// on purpose.
if (caps.isTextureCubeMapSupported()) {
if (caps.isAutomaticMipmapsSupported()) {
for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) {
// ensure the buffer is ready for reading
image.getData(face.ordinal()).rewind();
// send top level to card
GL11.glTexImage2D(getGLCubeMapFace(face), 0,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getWidth(), hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()),
image.getData(face.ordinal()));
}
} else {
for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) {
// ensure the buffer is ready for reading
image.getData(face.ordinal()).rewind();
// send to card
GLU.gluBuild2DMipmaps(getGLCubeMapFace(face),
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
image.getWidth(), image.getWidth(),
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()),
image.getData(face.ordinal()));
}
}
} else {
logger.warning("This card does not support Cubemaps.");
return;
}
break;
}
if (texture.getTextureMaxLevel() >= 0) {
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, texture.getTextureMaxLevel());
}
} else {
// Here we handle textures that are either compressed or have predefined mipmaps.
// Get mipmap data sizes and amount of mipmaps to send to opengl. Then loop through all mipmaps and send
// them.
int[] mipSizes = image.getMipMapByteSizes();
ByteBuffer data = null;
if (type == Type.CubeMap) {
if (caps.isTextureCubeMapSupported()) {
for (final TextureCubeMap.Face face : TextureCubeMap.Face.values()) {
data = image.getData(face.ordinal());
int pos = 0;
int max = 1;
if (mipSizes == null) {
mipSizes = new int[] { data.capacity() };
} else if (texture.getMinificationFilter().usesMipMapLevels()) {
max = mipSizes.length;
}
// set max mip level
GL11.glTexParameteri(getGLCubeMapFace(face), GL12.GL_TEXTURE_MAX_LEVEL, max - 1);
for (int m = 0; m < max; m++) {
final int width = Math.max(1, image.getWidth() >> m);
final int height = Math.max(1, image.getHeight() >> m);
data.position(pos);
data.limit(pos + mipSizes[m]);
if (texture.getTextureStoreFormat().isCompressed()) {
ARBTextureCompression.glCompressedTexImage2DARB(getGLCubeMapFace(face), m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, height, hasBorder ? 1 : 0, data);
} else {
GL11.glTexImage2D(getGLCubeMapFace(face), m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, height, hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data);
}
pos += mipSizes[m];
}
}
} else {
logger.warning("This card does not support CubeMaps.");
return;
}
} else {
data = image.getData(0);
int pos = 0;
int max = 1;
if (mipSizes == null) {
mipSizes = new int[] { data.capacity() };
} else if (texture.getMinificationFilter().usesMipMapLevels()) {
max = mipSizes.length;
}
// Set max mip level
switch (type) {
case TwoDimensional:
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, max - 1);
break;
case ThreeDimensional:
GL11.glTexParameteri(GL12.GL_TEXTURE_3D, GL12.GL_TEXTURE_MAX_LEVEL, max - 1);
break;
case OneDimensional:
GL11.glTexParameteri(GL11.GL_TEXTURE_1D, GL12.GL_TEXTURE_MAX_LEVEL, max - 1);
break;
}
if (type == Type.ThreeDimensional) {
if (caps.isTexture3DSupported()) {
// concat data into single buffer:
int dSize = 0;
int count = 0;
for (int x = 0; x < image.getData().size(); x++) {
if (image.getData(x) != null) {
data = image.getData(x);
dSize += data.limit();
count++;
}
}
// reuse buffer if we can.
if (count != 1) {
data = BufferUtils.createByteBuffer(dSize);
for (int x = 0; x < image.getData().size(); x++) {
if (image.getData(x) != null) {
data.put(image.getData(x));
}
}
// ensure the buffer is ready for reading
data.flip();
}
} else {
logger.warning("This card does not support Texture3D.");
return;
}
}
for (int m = 0; m < max; m++) {
final int width = Math.max(1, image.getWidth() >> m);
final int height = Math.max(1, image.getHeight() >> m);
data.position(pos);
data.limit(pos + mipSizes[m]);
switch (type) {
case TwoDimensional:
if (texture.getTextureStoreFormat().isCompressed()) {
ARBTextureCompression.glCompressedTexImage2DARB(GL11.GL_TEXTURE_2D, m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, height, hasBorder ? 1 : 0, data);
} else {
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, height, hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data);
}
break;
case OneDimensional:
if (texture.getTextureStoreFormat().isCompressed()) {
ARBTextureCompression.glCompressedTexImage1DARB(GL11.GL_TEXTURE_1D, m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, hasBorder ? 1 : 0, data);
} else {
GL11.glTexImage1D(GL11.GL_TEXTURE_1D, m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data);
}
break;
case ThreeDimensional:
final int depth = Math.max(1, image.getDepth() >> m);
// already checked for support above...
if (texture.getTextureStoreFormat().isCompressed()) {
ARBTextureCompression.glCompressedTexImage3DARB(GL12.GL_TEXTURE_3D, m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, height, depth, hasBorder ? 1 : 0, data);
} else {
GL12.glTexImage3D(GL12.GL_TEXTURE_3D, m,
LwjglTextureUtil.getGLInternalFormat(texture.getTextureStoreFormat()),
width, height, depth, hasBorder ? 1 : 0,
LwjglTextureUtil.getGLPixelFormat(image.getDataFormat()),
LwjglTextureUtil.getGLPixelDataType(image.getDataType()), data);
}
break;
}
pos += mipSizes[m];
}