There used to be devil that does this. I’m not sure how to do this anymore. I think JOGL has some support for it, but I’m using LWJGL.
Any help would be appreciated. I’m trying to load S3TC textures into OpenGL.
There used to be devil that does this. I’m not sure how to do this anymore. I think JOGL has some support for it, but I’m using LWJGL.
Any help would be appreciated. I’m trying to load S3TC textures into OpenGL.
You have to do it on your own.
One time I wrote a pretty simple loader for DDS which is far from complete. Maybe the code helps ya with the problem.
The classes will not compile with the resting file io, but for the general understanding of DDS/DXTn I think the code will help anyway
package com.evildevil.bubble.core.texture;
final class DDPixelFormat implements IDDSurface {
protected int size = 0; // this must be set to 32!
protected int flags = 0;
protected int fourCC = 0;
private String fourCCString = "";
protected int rgbBitCount = 0;
protected int rBitMask = 0;
protected int gBitMask = 0;
protected int bBitMask = 0;
protected int rgbAlphaBitMask = 0;
protected boolean isCompressed = true;
public DDPixelFormat() {
}
public void setSize(int size) throws TextureFormatException {
if (size != 32)
throw new TextureFormatException("Wrong DDPixelFormat size. DDPixelFormat size must be 32!");
this.size = size;
}
public void setFlags(int flags) {
this.flags = flags;
// check for (un)compressed
if ((flags & DDPF_RGB) == DDPF_RGB)
this.isCompressed = false;
else if ((flags & DDPF_FOURCC) == DDPF_FOURCC)
this.isCompressed = true;
}
public void setFourCC(int fourCC) {
this.fourCC = fourCC;
/*if (this.isCompressed)*/
createFourCCString(); // create the fourCCString
}
private void createFourCCString() {
byte[] fourCCString = new byte[DDPF_FOURCC];
fourCCString[0] = (byte)this.fourCC;
fourCCString[1] = (byte)(this.fourCC >> 8);
fourCCString[2] = (byte)(this.fourCC >> 16);
fourCCString[3] = (byte)(this.fourCC >> 24);
// fourCCString is done :)
this.fourCCString = new String(fourCCString);
}
public String getFourCCString() {
return this.fourCCString;
}
public void setRGBBitCount(int rgbBitCount) {
this.rgbAlphaBitMask = rgbBitCount;
}
public void setRBitMask(int rBitMask) {
this.rBitMask = rBitMask;
}
public void setGBitMask(int gBitMask) {
this.gBitMask = gBitMask;
}
public void setBBitMask(int bBitMask) {
this.bBitMask = bBitMask;
}
public void setRGBAlphaBitMask(int rgbAlphaBitMask) {
this.rgbAlphaBitMask = rgbAlphaBitMask;
}
}
package com.evildevil.bubble.core.texture;
final class DDSCaps2 implements IDDSurface {
public DDSCaps2() {
}
protected int caps1 = 0;
protected int caps2 = 0;
protected int reserved = 0; // unused by documentation
protected boolean isVolumeTexture = false;
public void setCaps1(int caps1) throws TextureFormatException {
this.caps1 = caps1;
if ((caps1 & DDSCAPS_TEXTURE) != DDSCAPS_TEXTURE) // check for DDSCAPS_TEXTURE
throw new TextureFormatException("DDS file does not contain DDSCAPS_TEXTURE, but it must!");
}
public void setCaps2(int caps2) {
this.caps2 = caps2;
// check for VolumeTexture
if ((caps2 & DDSCAPS2_VOLUME) == DDSCAPS2_VOLUME)
this.isVolumeTexture = true;
}
}
package com.evildevil.bubble.core.texture;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import static org.lwjgl.opengl.EXTTextureCompressionS3TC.*;
import static org.lwjgl.opengl.GL11.GL_LINEAR;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MAG_FILTER;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_MIN_FILTER;
import static org.lwjgl.opengl.GL11.glTexParameteri;
public final class DDSLoader implements IDDSurface {
private final String DDS_IDENTIFIER = "DDS ";
private final int DDS_HEADER_SIZE = 128; // size of the dds header
private final int DDS_DESC2_RESERVED_1 = 44; // bytesize of DWORD[11]
private final int DDS_DESC2_RESERVED_2 = 4; // bytesize of DWORD
private final int DDS_CAPS2_RESERVED = 8; // bytesize of DWORD[2]
private final int DEFAULT_DXT_BLOCKSIZE = 16;
private final int DXT1_BLOCKSIZE = 8;
private DDSurfaceDesc2 ddsDesc2 = null;
private ByteBuffer ddsHeader = null;
private FileChannel ddsFileChannel = null;
public DDSLoader() {
}
public int loadDDSFile(String fileName) {
// our DDS file
File ddsFile = new File(fileName);
try {
FileInputStream fis = new FileInputStream(ddsFile);
// assign the filechannel for reading data from it
ddsFileChannel = fis.getChannel();
// check for null
if (ddsFileChannel == null)
throw new NullPointerException("ddsFileChannel couldn't be null!");
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
// create a new DDSurfaceDesc2 object to hold all dds file information
ddsDesc2 = new DDSurfaceDesc2();
// allocate enough memory for storing the whole header
ddsHeader = BufferUtils.createByteBuffer(DDS_HEADER_SIZE);
readFileHeader();
int glName = readFileData();
return glName;
}
private void readFileHeader() {
try {
// read the header
ddsFileChannel.read(ddsHeader);
ddsHeader.rewind();
// read and feed the DDSurfaceDesc2
ddsDesc2.setIdentifier(ddsHeader.getInt());
ddsDesc2.setSize(ddsHeader.getInt());
ddsDesc2.setFlags(ddsHeader.getInt());
ddsDesc2.setHeight(ddsHeader.getInt());
ddsDesc2.setWidth(ddsHeader.getInt());
ddsDesc2.setPitchOrLinearSize(ddsHeader.getInt());
ddsDesc2.setDepth(ddsHeader.getInt());
ddsDesc2.setMipMapCount(ddsHeader.getInt());
// skip, cause next is unused
ddsHeader.position(ddsHeader.position()+DDS_DESC2_RESERVED_1);
// DDPixelFormat of DDSurfaceDesc2
DDPixelFormat pixelFormat = ddsDesc2.getDDPixelformat();
pixelFormat.setSize(ddsHeader.getInt());
pixelFormat.setFlags(ddsHeader.getInt());
pixelFormat.setFourCC(ddsHeader.getInt());
pixelFormat.setRGBBitCount(ddsHeader.getInt());
pixelFormat.setRBitMask(ddsHeader.getInt());
pixelFormat.setGBitMask(ddsHeader.getInt());
pixelFormat.setBBitMask(ddsHeader.getInt());
pixelFormat.setRGBAlphaBitMask(ddsHeader.getInt());
// DDSCaps2 of DDSurfaceDesc2
DDSCaps2 caps2 = ddsDesc2.getDDSCaps2();
caps2.setCaps1(ddsHeader.getInt());
caps2.setCaps2(ddsHeader.getInt());
// skip, cause next is unused
ddsHeader.position(ddsHeader.position()+DDS_CAPS2_RESERVED);
// we don't wanna read the last 4 bytes, they are not used anyway,
// but we skip them. Funny, ain't?
ddsHeader.position(ddsHeader.position()+DDS_DESC2_RESERVED_2);
// the last two instuctions might be banned, but thats your decission
} catch (BufferUnderflowException bue) {
bue.printStackTrace();
} catch (TextureFormatException tfe) {
tfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
ddsHeader = null; // free the memory
}
}
private int readFileData() {
final DDPixelFormat ddpf = ddsDesc2.getDDPixelformat();
int imageSize = 0;
int dxtFormat = 0;
// calculate the image size depending on the used blocksize
// and set the used DXT format
if (ddpf.isCompressed && ddpf.getFourCCString().equalsIgnoreCase("DXT1")) {
imageSize = calculateSize(DXT1_BLOCKSIZE);
// at the moment we treat any DXT1 image as RGBA,
// maybe this can be switched dynamically in future...
dxtFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
} else {
imageSize = calculateSize(DEFAULT_DXT_BLOCKSIZE);
if (ddpf.getFourCCString().equalsIgnoreCase("DXT3"))
dxtFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
else if(ddpf.getFourCCString().equals("DXT5"))
dxtFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
// read the dds file data itself
ByteBuffer imageData = BufferUtils.createByteBuffer(ddsDesc2.pitchOrLinearSize);
try {
ddsFileChannel.read(imageData);
imageData.rewind();
} catch (IOException ioe) {
ioe.printStackTrace();
}
// create the GL Name
IntBuffer glName = BufferUtils.createIntBuffer(1);
// create the texture
GL11.glGenTextures(glName);
GL11.glBindTexture(GL11.GL_TEXTURE_2D,glName.get(0));
// Implement the filtering stuff anywhere you want, this is only here to
// have at least one filter applied on the texture
// Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_S,GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_T,GL11.GL_REPEAT);
GL13.glCompressedTexImage2D(GL11.GL_TEXTURE_2D,0,dxtFormat,ddsDesc2.width,ddsDesc2.height,0,ddsDesc2.pitchOrLinearSize,imageData);
return glName.get(0);
}
private int calculateSize(int blockSize) {
double size = Math.ceil(ddsDesc2.width/4) * Math.ceil(ddsDesc2.height/4) * blockSize;
return (int)size;
}
public void debugInfo() {
DDPixelFormat pixelFormat = ddsDesc2.getDDPixelformat();
DDSCaps2 caps2 = ddsDesc2.getDDSCaps2();
System.out.println("\nDDSURFACEDESC2:");
System.out.println("----------------------------------------");
System.out.println("SIZE: "+ddsDesc2.size);
System.out.println("FLAGS: "+ddsDesc2.flags);
System.out.println("HEIGHT: "+ddsDesc2.height);
System.out.println("WIDTH: "+ddsDesc2.width);
System.out.println("PITCH_OR_LINEAR_SIZE: "+ddsDesc2.pitchOrLinearSize);
System.out.println("DEPTH: "+ddsDesc2.depth);
System.out.println("MIP_MAP_COUNT: "+ddsDesc2.mipMapCount);
System.out.println("\nDDPIXELFORMAT of DDSURFACEDESC2:");
System.out.println("----------------------------------------");
System.out.println("SIZE :"+pixelFormat.size);
System.out.println("FLAGS: "+pixelFormat.flags);
System.out.println("FOUR_CC: "+pixelFormat.getFourCCString());
System.out.println("RGB_BIT_COUNT: "+pixelFormat.rgbBitCount);
System.out.println("R_BIT_MASK: "+pixelFormat.rBitMask);
System.out.println("G_BIT_MASK: "+pixelFormat.gBitMask);
System.out.println("B_BIT_MASK: "+pixelFormat.bBitMask);
System.out.println("RGB_ALPHA_BIT_MASK: "+pixelFormat.rgbAlphaBitMask);
System.out.println("\nDDSCAPS of DDSURFACEDESC2");
System.out.println("----------------------------------------");
System.out.println("CAPS1: "+caps2.caps1);
System.out.println("CAPS2: "+caps2.caps2);
}
}
package com.evildevil.bubble.core.texture;
// JAVA Imports
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.opengl.EXTTextureCompressionS3TC.*;
import org.lwjgl.opengl.ARBTextureCompression;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
import com.evildevil.bubble.core.LogManager;
import com.evildevil.bubble.util.BufferUtils;
public final class DDSTextureLoader extends TextureLoader {
private static final long DDSD_MIPMAPCOUNT = 0x0002000L;
private byte[] dxtHeader = new byte[128]; // the header of a DXT file
private DDSurfaceDesc2 dds = null;
protected enum dxtType {
DXT1, DXT3, DXT5
};
public DDSTextureLoader() {
//LogManager.log(GL11.glGetString(GL11.GL_VENDOR));
//LogManager.log(GL11.glGetString(GL11.GL_RENDERER));
//LogManager.log(GL11.glGetString(GL11.GL_VERSION));
}
public Texture loadTexture(String textureName) throws TextureFormatException {
// call TextureLoader::loadTexture to make everything smooth :)=
super.loadTexture(textureName);
// read the header
dxtHeader = this.getHeader(dxtHeader.length);
// ensure that a new dds object will be created, if allready one exists or not yet!
if (dds != null || dds == null)
dds = new DDSurfaceDesc2();
LogManager.log("Loading DXT Texture: "+textureName);
dds.createSurfaceDesc2(dxtHeader);
dds.debug();
loadDDSTexture(textureName);
return texture;
}
private int loadDDSTexture(String textureName) {
// check if we really have a dds survace
String ddsIdentifier = "DDS ";
//if (!ddsIdentifier.equals(dds.identifier) || dds.size != 124 || dds.size != 32)
// throw new TextureFormatException("texture is not a valid dds survace");
int dxtFormat = 0;
// which survace format does we have here?^
//System.out.println("DXT_FORMAT: "+GL_COMPRESSED_RGB_S3TC_DXT1_EXT);
//System.out.println("DXT_FORMAT: "+GL_COMPRESSED_RGBA_S3TC_DXT1_EXT);
//System.out.println("DXT_FORMAT: "+GL_COMPRESSED_RGBA_S3TC_DXT3_EXT);
//System.out.println("DXT_FORMAT: "+GL_COMPRESSED_RGBA_S3TC_DXT5_EXT);
//System.out.println("DDS Alpha: "+dds.);
if (dds.getDDPixelformat().getFourCCString().compareTo("DXT1") == 0) {
dxtFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
System.out.println((dds.getDDPixelformat().flags & IDDSurface.DDPF_APHAPIXELS) == IDDSurface.DDPF_APHAPIXELS);
} else if (dds.getDDPixelformat().getFourCCString().compareTo("DXT3") == 0)
dxtFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
else if (dds.getDDPixelformat().getFourCCString().compareTo("DXT5") == 0)
dxtFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
int blockSize = 16;
if (dxtFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
blockSize = 8;
//int size = ((dds.width + 3) / 4) * ((dds.height + 3) / 4) * ((dds.depth + 3) / 4) * blockSize;
int size = (dds.width / 4) * (dds.height / 4) * (dds.depth / 4) * blockSize;
//LogManager.logDebug("Size1: "+size+" linearSize: "+dds.linearSize);
//LogManager.logDebug(""+((dds.width/4)*(dds.height/4)*blockSize));
// mipmaps?
if ((dds.flags & DDSD_MIPMAPCOUNT) == 0 || dds.mipMapCount == 0)
dds.mipMapCount = 1;
// do caps 2 check
DDSCaps2 caps2 = dds.getDDSCaps2();
if (IDDSurface.DDSCAPS2_CUBEMAP == (caps2.caps2 & IDDSurface.DDSCAPS2_CUBEMAP))
LogManager.log("Cubemap found");
if (IDDSurface.DDSCAPS2_CUBEMAP_POSITIVEX == (caps2.caps2 & IDDSurface.DDSCAPS2_CUBEMAP_POSITIVEX))
LogManager.log("Cubemap DDSCAPS2_CUBEMAP_POSITIVEX found");
if (IDDSurface.DDSCAPS2_CUBEMAP_NEGATIVEX == (caps2.caps2 & IDDSurface.DDSCAPS2_CUBEMAP_NEGATIVEX))
LogManager.log("Cubemap DDSCAPS2_CUBEMAP_NEGATIVEX found");
if (IDDSurface.DDSCAPS2_CUBEMAP_POSITIVEY == (caps2.caps2 & IDDSurface.DDSCAPS2_CUBEMAP_POSITIVEY))
LogManager.log("Cubemap DDSCAPS2_CUBEMAP_POSITIVEY found");
if (IDDSurface.DDSCAPS2_CUBEMAP_NEGATIVEY == (caps2.caps2 & IDDSurface.DDSCAPS2_CUBEMAP_NEGATIVEY))
LogManager.log("Cubemap DDSCAPS2_CUBEMAP_NEGATIVEY found");
if (IDDSurface.DDSCAPS2_CUBEMAP_POSITIVEZ == (caps2.caps2 & IDDSurface.DDSCAPS2_CUBEMAP_POSITIVEZ))
LogManager.log("Cubemap DDSCAPS2_CUBEMAP_POSITIVEZ found");
if (IDDSurface.DDSCAPS2_CUBEMAP_NEGATIVEZ == (caps2.caps2 & IDDSurface.DDSCAPS2_CUBEMAP_NEGATIVEZ))
LogManager.log("Cubemap DDSCAPS2_CUBEMAP_NEGATIVEZ found");
// load and finish
final byte[] surfaceData = loadTextureData(dds.pitchOrLinearSize,true);
final byte[] surfaceData2 = new byte[surfaceData.length];
//System.out.println(surfaceData.length);
//System.out.println(surfaceData2.length);
// swap the surface data
// data for one row
final int rowSize = dds.width * texture.bpp;
//for (int i=0; i<surfaceData2.length; i+=rowSize) {
// for (int c=0; c<rowSize; c++) {
// surfaceData2[c] = surfaceData[surfaceData.length-(i*rowSize)+c];
//}
//}
ByteBuffer surfaceBuffer = BufferUtils.createByteBuffer(surfaceData);
IntBuffer glName = generateGLName();
generateAndBindTexture(glName);
//setLinearFilter();
setNearestFilter();
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_S,GL11.GL_REPEAT);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_WRAP_T,GL11.GL_REPEAT);
System.out.println("ARB_Texture_Compression: "+GLContext.getCapabilities().GL_ARB_texture_compression);
System.out.println("OpenGL 13: "+GLContext.getCapabilities().OpenGL13);
//GL13.glCompressedTexImage2D(GL11.GL_TEXTURE_2D,0,dxtFormat,dds.width,dds.height,0,dds.pitchOrLinearSize,surfaceBuffer);
ARBTextureCompression.glCompressedTexImage2DARB(GL11.GL_TEXTURE_2D,0,dxtFormat,dds.width,dds.height,0,dds.pitchOrLinearSize,surfaceBuffer);
texture.setGLName(glName.get(0));
return glName.get(0);
}
// @see com.evildevil.bubble.texture.TextureLoader#getTextureFormat()
public void getTextureFormat() throws TextureFormatException {
}
}
uuuh, 2005/2006…quite some time…
//edit: removed multiline comments. though a credit when using stuff from it would be nice
Remove the multi-line comments since the Java syntax color script is bugged. Only single line comments work.
Nice stuff. Is this any recent though? Does it load BPTC compressed files?
Is BPTC the new format microsoft started with DX11? will try google
//edit: Wow, the BPTC is impressive. But as the code was done in 2005/2006 when there was no DX11 and BPTC it is not supported
But with the spec files it should not be that hard to write a simple loader.
http://www.g-truc.net/post-0340.html
http://www.opengl.org/registry/specs/ARB/texture_compression_rgtc.txt
http://www.opengl.org/registry/specs/ARB/texture_compression_bptc.txt
I guess there’ll be more sites with indepth information like pseudo code and such stuff.